Skip to content

Commit

Permalink
2018-10-31 DB: added MongoDB 4 examples using the latest PHP MongoDB …
Browse files Browse the repository at this point in the history
…driver
  • Loading branch information
dbierer committed Oct 31, 2018
1 parent 817ff40 commit 8d52b97
Show file tree
Hide file tree
Showing 146 changed files with 2,296 additions and 0 deletions.
7 changes: 7 additions & 0 deletions mongoDB_3/README.md
@@ -0,0 +1,7 @@
# MongoDB 3 Examples

* The examples in this repo are mainly based on work which I did in 2013. It's based on MongoDB 3.4 and uses the old (and now deprecated) `Mongo` PHP Database driver.
* For more information about this driver see: http://php.net/manual/en/book.mongo.php.
* If you are starting a new project, it is recommended that you use MongoDB 4 or above, and the currently maintained `MongoDB` driver
* For more information about the new driver see: http://php.net/manual/en/set.mongodb.php
* Look for a directory `mongoDB_4` for examples using this driver
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions mongodb_4/.gitignore
@@ -0,0 +1 @@
vendor/
143 changes: 143 additions & 0 deletions mongodb_4/Application/Add.php
@@ -0,0 +1,143 @@
<?php
namespace Application;

use Throwable;
use MongoDB\Model\BSONDocument;

/**
* Used by add.php to add a new purchase to the database
*/
class Add extends Base
{

/**
* Returns customer document
*
* @return MongoDB\Model\BSONDocument $customer
*/
public function findCustomerByName($name)
{

// build projection: these are the fields we will include in the purchase
$options = [
'projection' => [
'_id' => 1,
'name' => 1,
'state_province' => 1,
'country' => 1,
'balance' => 1,
'purch_history' => 1
]
];
// perform find
$result = NULL;
try {
$result = $this->customers->findOne(['name' => $name], $options);
} catch (Throwable $e) {
error_log(__METHOD__ . ':' . $e->getMessage());
}
return $result;
}
/**
* Returns product document
*
* @return MongoDB\Model\BSONDocument $product
*/
public function findProductBySku($sku)
{

// build projection: in the purchase we suppress the description
$options = ['projection' => ['description' => 0]];
// perform find
$result = NULL;
try {
$result = $this->products->findOne(['sku' => $sku], $options);
} catch (Throwable $e) {
error_log(__METHOD__ . ':' . $e->getMessage());
}
return $result;
}
/**
* Insert new purchase without transaction support
*
* @param MongoDB\Model\BSONDocument $customer
* @param MongoDB\Model\BSONDocument $product
* @param int $quantity
* @return boolean $result
*/
public function savePurchase(BSONDocument $customer, BSONDocument $product, $quantity)
{
$result = FALSE;
$date = date(self::DATE_FORMAT);
$session = $this->connection->getSession();
$data = [
'customer' => $customer,
'product' => $product,
'date' => $date,
'quantity' => $quantity,
'amount' => $quantity * (float) $product->price
];
try {
if ($result = $this->purchases->insertOne($data)) {
// add date to customer purchase history
$list = $customer->purch_history;
$list[] = $date;
$this->customers->updateOne(
['name' => $customer->name],
['$set' => ['purch_history' => $list]]);
}
} catch (Throwable $e) {
$result = FALSE;
error_log(__METHOD__ . ':' . $e->getMessage());
}
return $result;
}
/**
* Insert new purchase with transaction support(only available in MongoDB v4.0+)
* See: https://docs.mongodb.com/manual/core/transactions/#transactions
* Can only run on a replica set!!!
*
* @param MongoDB\Model\BSONDocument $customer
* @param MongoDB\Model\BSONDocument $product
* @param int $quantity
* @return boolean $result
*/
public function savePurchaseWithSession(BSONDocument $customer, BSONDocument $product, $quantity)
{
// init vars
$result = FALSE;
$date = date(self::DATE_FORMAT);

// get MongoDB\Driver\Session which is used for transaction support
$session = $this->connection->getSession();
$data = [
'customer' => $customer,
'product' => $product,
'date' => $date,
'quantity' => $quantity,
'amount' => $quantity * (float) $product->price
];
try {
// begin transaction
$session->startTransaction();
// need to add session as an option to get transaction support
if ($result = $this->purchases->insertOne($data, ['session' => $session])) {
$list = $customer->purch_history;
$list[] = $date;
$this->customers->updateOne(
['name' => $customer->name],
['$set' => ['purch_history' => $list]],
// need to add session as an option to get transaction support
['session' => $session]);
}
// commit
$session->commitTransaction();
} catch (Throwable $e) {
// rollback
$session->abortTransaction();
$result = FALSE;
error_log(__METHOD__ . ':' . $e->getMessage());
}
return $result;
}
}
38 changes: 38 additions & 0 deletions mongodb_4/Application/Base.php
@@ -0,0 +1,38 @@
<?php
namespace Application;

/**
* Serves as base class for Main, Lookup and Add
*/
class Base
{

const DATE_FORMAT = 'Y-m-d';
const DEFAULT_URL = '/json.php';
const DEFAULT_LIMIT = 100;
const DEFAULT_END_DATE = 'P99Y';

protected $connection;
protected $products;
protected $purchases;
protected $customers;

public function __construct($config)
{
$this->connection = new Connection($config);
$client = $this->connection->getClient();
$this->products = $client->sweetscomplete->products;
$this->purchases = $client->sweetscomplete->purchases;
$this->customers = $client->sweetscomplete->customers;
}
/**
* Gives access to Application\Connection
* which in turn gives access to the client, manager and session
*
* @return Application\Connection $connection
*/
public function getConnection()
{
return $this->connection;
}
}
52 changes: 52 additions & 0 deletions mongodb_4/Application/Client.php
@@ -0,0 +1,52 @@
<?php
namespace Application;

use Exception;
use MongoDB\Client as MongoClient;
/**
* Creates instance of MongoDB\Client
* Only accepts 1 host
*/
class Client
{

const ERROR_HOST = 'ERROR: host must be specified';
const ERROR_OPTS = 'ERROR: options must be an array even if only one';
protected $uri;
protected $mongoClient;
public function __construct($config)
{
$this->uri = $this->buildUri($config);
$this->mongoClient = new MongoClient($this->uri);
}
public function getClient()
{
return $this->mongoClient;
}
// see: https://docs.mongodb.com/manual/reference/connection-string/#standard-connection-string-format
public function buildUri($config)
{
$uri = 'mongodb://';
if (isset($config['username']) && isset($config['password'])) {
$uri .= $config['username'] . ':' . $config['password'] . '@';
}
if (!isset($config['host'])) {
throw new Exception(self::ERROR_HOST);
}
$uri .= $config['host'];
$uri .= (isset($config['port'])) ? ':' . $config['port'] : '';
$uri .= (isset($config['database'])) ? '/' . $config['database'] : '';
if (isset($config['options'])) {
if (!is_array($config['options'])) {
throw new Exception(self::ERROR_OPTS);
}
$uri .= '?';
foreach ($config['options'] as $key => $value) {
$uri .= $key . '=' . $value . '&';
}
// trim trailing '&'
$uri = substr($uri, 0, -1);
}
return $uri;
}
}
64 changes: 64 additions & 0 deletions mongodb_4/Application/Connection.php
@@ -0,0 +1,64 @@
<?php
namespace Application;

use Exception;
use MongoDB\Client as MongoClient;
/**
* Creates instance of MongoDB\Client
* Only accepts 1 host
*/
class Connection
{

const ERROR_HOST = 'ERROR: host must be specified';
const ERROR_OPTS = 'ERROR: options must be an array even if only one';
const ERROR_CONFIG_URI = 'ERROR: must include a key "uri" in config with MongoDB connection info';

protected $mongoClient;
protected $manager; // MongoDB\Driver\Manager
public function __construct($config)
{
if (!isset($config['uri'])) {
throw new Exception(self::ERROR_CONFIG_URI);
}
$uriOpts = $config['uriOpts'] ?? [];
$driverOpts = $config['driverOpts'] ?? [];
$uri = $this->buildUri($config);
$this->mongoClient = new MongoClient($uri, $uriOpts, $driverOpts);
$this->manager = $this->mongoClient->getManager();
}
public function getClient()
{
return $this->mongoClient;
}
public function getManager()
{
return $this->manager;
}
public function getSession()
{
return $this->manager->startSession();
}
// see: https://docs.mongodb.com/manual/reference/connection-string/#standard-connection-string-format
public function buildUri($config)
{
$uri = 'mongodb://';
if (isset($config['uri']['username']) && isset($config['uri']['password']))
$uri .= $config['uri']['username'] . ':' . $config['uri']['password'] . '@';
if (!isset($config['uri']['host']))
throw new Exception(self::ERROR_HOST);
$uri .= $config['uri']['host'];
$uri .= (isset($config['uri']['port'])) ? ':' . $config['uri']['port'] : '';
$uri .= (isset($config['uri']['database'])) ? '/' . $config['uri']['database'] : '';
if (isset($config['uriOpts'])) {
if (!is_array($config['uriOpts']))
throw new Exception(self::ERROR_OPTS);
$uri .= '?';
foreach ($config['uriOpts'] as $key => $value)
$uri .= $key . '=' . $value . '&';
// trim trailing '&'
$uri = substr($uri, 0, -1);
}
return $uri;
}
}
40 changes: 40 additions & 0 deletions mongodb_4/Application/Csv.php
@@ -0,0 +1,40 @@
<?php
namespace Application;

use Exception;
use SplFileObject;
class Csv
{
const ERROR_FILE = 'ERROR: file does not exist';
protected $csvFile;
protected $headers;
public function __construct($fn)
{
if (!file_exists($fn)) throw new Exception(self::ERROR_FILE);
$this->csvFile = new SplFileObject($fn, 'r');
}
public function getHeaders()
{
if (!$this->headers) {
$this->csvFile->rewind();
$this->headers = $this->csvFile->fgetcsv();
}
return $this->headers;
}
public function getIterator()
{
$this->csvFile->rewind();
while ($line = $this->csvFile->fgetcsv()) {
if (is_array($line)) yield $line;
}
}
public function getIteratorWithHeaders()
{
foreach ($this->getIterator() as $line) {
if (count($this->getHeaders()) == count($line)) {
yield array_combine($this->getHeaders(), $line);
}
}
}
}

0 comments on commit 8d52b97

Please sign in to comment.