laravel mongodb 支持
commit
405bac0668
@ -0,0 +1 @@
|
||||
/vendor/
|
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ComposerJsonPluginSettings">
|
||||
<unboundedVersionInspectionSettings>
|
||||
<excludedPackages />
|
||||
</unboundedVersionInspectionSettings>
|
||||
<customRepositories />
|
||||
<composerUpdateOptions />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PublishConfigData">
|
||||
<serverData>
|
||||
<paths name="_development">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
<paths name="_staging">
|
||||
<serverdata>
|
||||
<mappings>
|
||||
<mapping local="$PROJECT_DIR$" web="/" />
|
||||
</mappings>
|
||||
</serverdata>
|
||||
</paths>
|
||||
</serverData>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="TouchWorld" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,5 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" />
|
||||
</settings>
|
||||
</component>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/laravel-mongodb.iml" filepath="$PROJECT_DIR$/.idea/laravel-mongodb.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,15 @@
|
||||
ARG PHP_VERSION=7.2
|
||||
ARG COMPOSER_VERSION=1.8
|
||||
|
||||
FROM composer:${COMPOSER_VERSION}
|
||||
FROM php:${PHP_VERSION}-cli
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y autoconf pkg-config libssl-dev git libzip-dev zlib1g-dev && \
|
||||
pecl install mongodb && docker-php-ext-enable mongodb && \
|
||||
pecl install xdebug && docker-php-ext-enable xdebug && \
|
||||
docker-php-ext-install -j$(nproc) pdo_mysql zip
|
||||
|
||||
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
WORKDIR /code
|
@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "ifornew/mongodb",
|
||||
"description": "A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"eloquent",
|
||||
"mongodb",
|
||||
"mongo",
|
||||
"database",
|
||||
"model",
|
||||
"moloquent",
|
||||
"transaction"
|
||||
],
|
||||
"homepage": "https://git.ifornew.com/laravel-mongodb.git",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ifornew",
|
||||
"homepage": "https://ifornew.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"illuminate/support": "^5.8|^6.0",
|
||||
"illuminate/container": "^5.8|^6.0",
|
||||
"illuminate/database": "^5.8|^6.0",
|
||||
"illuminate/events": "^5.8|^6.0",
|
||||
"mongodb/mongodb": "^1.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0|^7.0|^8.0",
|
||||
"orchestra/testbench": "^3.1|^4.0",
|
||||
"mockery/mockery": "^1.0",
|
||||
"satooshi/php-coveralls": "^2.0",
|
||||
"doctrine/dbal": "^2.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"Ifornew\\": "src"}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"tests/TestCase.php",
|
||||
"tests/models",
|
||||
"tests/seeds"
|
||||
]
|
||||
},
|
||||
"suggest": {
|
||||
"jenssegers/mongodb-session": "Add MongoDB session support to Laravel-MongoDB",
|
||||
"jenssegers/mongodb-sentry": "Add Sentry support to Laravel-MongoDB"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Ifornew\\Mongodb\\MongodbServiceProvider",
|
||||
"Ifornew\\Mongodb\\MongodbQueueServiceProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
tests:
|
||||
container_name: tests
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- .:/code
|
||||
working_dir: /code
|
||||
depends_on:
|
||||
- mongodb
|
||||
- mysql
|
||||
|
||||
mysql:
|
||||
container_name: mysql
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD:
|
||||
MYSQL_DATABASE: unittest
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
||||
logging:
|
||||
driver: none
|
||||
|
||||
mongodb:
|
||||
container_name: mongodb
|
||||
image: mongo
|
||||
ports:
|
||||
- 27017:27017
|
||||
logging:
|
||||
driver: none
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Auth;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Illuminate\Auth\Passwords\DatabaseTokenRepository as BaseDatabaseTokenRepository;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
class DatabaseTokenRepository extends BaseDatabaseTokenRepository
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getPayload($email, $token)
|
||||
{
|
||||
return [
|
||||
'email' => $email,
|
||||
'token' => $this->hasher->make($token),
|
||||
'created_at' => new UTCDateTime(time() * 1000),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function tokenExpired($createdAt)
|
||||
{
|
||||
// Convert UTCDateTime to a date string.
|
||||
if ($createdAt instanceof UTCDateTime) {
|
||||
$date = $createdAt->toDateTime();
|
||||
$date->setTimezone(new DateTimeZone(date_default_timezone_get()));
|
||||
$createdAt = $date->format('Y-m-d H:i:s');
|
||||
} elseif (is_array($createdAt) && isset($createdAt['date'])) {
|
||||
$date = new DateTime($createdAt['date'], new DateTimeZone(isset($createdAt['timezone']) ? $createdAt['timezone'] : 'UTC'));
|
||||
$date->setTimezone(new DateTimeZone(date_default_timezone_get()));
|
||||
$createdAt = $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
return parent::tokenExpired($createdAt);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Auth;
|
||||
|
||||
use Illuminate\Auth\Passwords\PasswordBrokerManager as BasePasswordBrokerManager;
|
||||
|
||||
class PasswordBrokerManager extends BasePasswordBrokerManager
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function createTokenRepository(array $config)
|
||||
{
|
||||
return new DatabaseTokenRepository(
|
||||
$this->app['db']->connection(),
|
||||
$this->app['hash'],
|
||||
$config['table'],
|
||||
$this->app['config']['app.key'],
|
||||
$config['expire']
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Auth;
|
||||
|
||||
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as BasePasswordResetServiceProvider;
|
||||
|
||||
class PasswordResetServiceProvider extends BasePasswordResetServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register the token repository implementation.
|
||||
* @return void
|
||||
*/
|
||||
protected function registerTokenRepository()
|
||||
{
|
||||
$this->app->singleton('auth.password.tokens', function ($app) {
|
||||
$connection = $app['db']->connection();
|
||||
|
||||
// The database token repository is an implementation of the token repository
|
||||
// interface, and is responsible for the actual storing of auth tokens and
|
||||
// their e-mail addresses. We will inject this table and hash key to it.
|
||||
$table = $app['config']['auth.password.table'];
|
||||
|
||||
$key = $app['config']['app.key'];
|
||||
|
||||
$expire = $app['config']->get('auth.password.expire', 60);
|
||||
|
||||
return new DatabaseTokenRepository($connection, $table, $key, $expire);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function registerPasswordBroker()
|
||||
{
|
||||
$this->app->singleton('auth.password', function ($app) {
|
||||
return new PasswordBrokerManager($app);
|
||||
});
|
||||
|
||||
$this->app->bind('auth.password.broker', function ($app) {
|
||||
return $app->make('auth.password')->broker();
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Auth;
|
||||
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
use Ifornew\Mongodb\Eloquent\Model;
|
||||
|
||||
class User extends Model implements
|
||||
AuthenticatableContract,
|
||||
AuthorizableContract,
|
||||
CanResetPasswordContract
|
||||
{
|
||||
use Authenticatable, Authorizable, CanResetPassword;
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb;
|
||||
|
||||
use Exception;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\Collection as MongoCollection;
|
||||
|
||||
class Collection
|
||||
{
|
||||
/**
|
||||
* The connection instance.
|
||||
* @var Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The MongoCollection instance..
|
||||
* @var MongoCollection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* @param Connection $connection
|
||||
* @param MongoCollection $collection
|
||||
*/
|
||||
public function __construct(Connection $connection, MongoCollection $collection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->collection = $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic method calls.
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
$start = microtime(true);
|
||||
$result = call_user_func_array([$this->collection, $method], $parameters);
|
||||
|
||||
if ($this->connection->logging()) {
|
||||
// Once we have run the query we will calculate the time that it took to run and
|
||||
// then log the query, bindings, and execution time so we will report them on
|
||||
// the event that the developer needs them. We'll log time in milliseconds.
|
||||
$time = $this->connection->getElapsedTime($start);
|
||||
|
||||
$query = [];
|
||||
|
||||
// Convert the query parameters to a json string.
|
||||
array_walk_recursive($parameters, function (&$item, $key) {
|
||||
if ($item instanceof ObjectID) {
|
||||
$item = (string) $item;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert the query parameters to a json string.
|
||||
foreach ($parameters as $parameter) {
|
||||
try {
|
||||
$query[] = json_encode($parameter);
|
||||
} catch (Exception $e) {
|
||||
$query[] = '{...}';
|
||||
}
|
||||
}
|
||||
|
||||
$queryString = $this->collection->getCollectionName() . '.' . $method . '(' . implode(',', $query) . ')';
|
||||
|
||||
$this->connection->logQuery($queryString, [], $time);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb;
|
||||
|
||||
use Illuminate\Database\Connection as BaseConnection;
|
||||
use Illuminate\Support\Arr;
|
||||
use MongoDB\Client;
|
||||
|
||||
class Connection extends BaseConnection
|
||||
{
|
||||
use TransactionTrait;
|
||||
|
||||
/**
|
||||
* The MongoDB database handler.
|
||||
* @var \MongoDB\Database
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* The MongoDB connection handler.
|
||||
* @var \MongoDB\Client
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Create a new database connection instance.
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
// Build the connection string
|
||||
$dsn = $this->getDsn($config);
|
||||
|
||||
// You can pass options directly to the MongoDB constructor
|
||||
$options = Arr::get($config, 'options', []);
|
||||
|
||||
// Create the connection
|
||||
$this->connection = $this->createConnection($dsn, $config, $options);
|
||||
|
||||
// Select database
|
||||
$this->db = $this->connection->selectDatabase($config['database']);
|
||||
|
||||
$this->useDefaultPostProcessor();
|
||||
|
||||
$this->useDefaultSchemaGrammar();
|
||||
|
||||
$this->useDefaultQueryGrammar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a fluent query against a database collection.
|
||||
* @param string $collection
|
||||
* @return Query\Builder
|
||||
*/
|
||||
public function collection($collection)
|
||||
{
|
||||
$query = new Query\Builder($this, $this->getPostProcessor());
|
||||
|
||||
return $query->from($collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a fluent query against a database collection.
|
||||
* @param string $table
|
||||
* @param string|null $as
|
||||
* @return Query\Builder
|
||||
*/
|
||||
public function table($table, $as = null)
|
||||
{
|
||||
return $this->collection($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MongoDB collection.
|
||||
* @param string $name
|
||||
* @return Collection
|
||||
*/
|
||||
public function getCollection($name)
|
||||
{
|
||||
return new Collection($this, $this->db->selectCollection($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSchemaBuilder()
|
||||
{
|
||||
return new Schema\Builder($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MongoDB database object.
|
||||
* @return \MongoDB\Database
|
||||
*/
|
||||
public function getMongoDB()
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* return MongoDB object.
|
||||
* @return \MongoDB\Client
|
||||
*/
|
||||
public function getMongoClient()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDatabaseName()
|
||||
{
|
||||
return $this->getMongoDB()->getDatabaseName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MongoDB connection.
|
||||
* @param string $dsn
|
||||
* @param array $config
|
||||
* @param array $options
|
||||
* @return \MongoDB\Client
|
||||
*/
|
||||
protected function createConnection($dsn, array $config, array $options)
|
||||
{
|
||||
// By default driver options is an empty array.
|
||||
$driverOptions = [];
|
||||
|
||||
if (isset($config['driver_options']) && is_array($config['driver_options'])) {
|
||||
$driverOptions = $config['driver_options'];
|
||||
}
|
||||
|
||||
// Check if the credentials are not already set in the options
|
||||
if (!isset($options['username']) && !empty($config['username'])) {
|
||||
$options['username'] = $config['username'];
|
||||
}
|
||||
if (!isset($options['password']) && !empty($config['password'])) {
|
||||
$options['password'] = $config['password'];
|
||||
}
|
||||
|
||||
return new Client($dsn, $options, $driverOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
unset($this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given configuration array has a dsn string.
|
||||
* @param array $config
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasDsnString(array $config)
|
||||
{
|
||||
return isset($config['dsn']) && !empty($config['dsn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DSN string form configuration.
|
||||
* @param array $config
|
||||
* @return string
|
||||
*/
|
||||
protected function getDsnString(array $config)
|
||||
{
|
||||
return $config['dsn'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DSN string for a host / port configuration.
|
||||
* @param array $config
|
||||
* @return string
|
||||
*/
|
||||
protected function getHostDsn(array $config)
|
||||
{
|
||||
// Treat host option as array of hosts
|
||||
$hosts = is_array($config['host']) ? $config['host'] : [$config['host']];
|
||||
|
||||
foreach ($hosts as &$host) {
|
||||
// Check if we need to add a port to the host
|
||||
if (strpos($host, ':') === false && !empty($config['port'])) {
|
||||
$host = $host . ':' . $config['port'];
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we want to authenticate against a specific database.
|
||||
$auth_database = isset($config['options']) && !empty($config['options']['database']) ? $config['options']['database'] : null;
|
||||
|
||||
return 'mongodb://' . implode(',', $hosts) . ($auth_database ? '/' . $auth_database : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DSN string from a configuration.
|
||||
* @param array $config
|
||||
* @return string
|
||||
*/
|
||||
protected function getDsn(array $config)
|
||||
{
|
||||
return $this->hasDsnString($config)
|
||||
? $this->getDsnString($config)
|
||||
: $this->getHostDsn($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getElapsedTime($start)
|
||||
{
|
||||
return parent::getElapsedTime($start);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getDriverName()
|
||||
{
|
||||
return 'mongodb';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDefaultPostProcessor()
|
||||
{
|
||||
return new Query\Processor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDefaultQueryGrammar()
|
||||
{
|
||||
return new Query\Grammar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getDefaultSchemaGrammar()
|
||||
{
|
||||
return new Schema\Grammar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically pass methods to the connection.
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
return call_user_func_array([$this->db, $method], $parameters);
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Eloquent;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Ifornew\Mongodb\Helpers\QueriesRelationships;
|
||||
use MongoDB\Driver\Cursor;
|
||||
use MongoDB\Model\BSONDocument;
|
||||
|
||||
class Builder extends EloquentBuilder
|
||||
{
|
||||
use QueriesRelationships;
|
||||
|
||||
/**
|
||||
* The methods that should be returned from query builder.
|
||||
* @var array
|
||||
*/
|
||||
protected $passthru = [
|
||||
'toSql',
|
||||
'insert',
|
||||
'insertGetId',
|
||||
'pluck',
|
||||
'count',
|
||||
'min',
|
||||
'max',
|
||||
'avg',
|
||||
'sum',
|
||||
'exists',
|
||||
'push',
|
||||
'pull',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function update(array $values, array $options = [])
|
||||
{
|
||||
// Intercept operations on embedded models and delegate logic
|
||||
// to the parent relation instance.
|
||||
if ($relation = $this->model->getParentRelation()) {
|
||||
$relation->performUpdate($this->model, $values);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $this->toBase()->update($this->addUpdatedAtColumn($values), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function insert(array $values)
|
||||
{
|
||||
// Intercept operations on embedded models and delegate logic
|
||||
// to the parent relation instance.
|
||||
if ($relation = $this->model->getParentRelation()) {
|
||||
$relation->performInsert($this->model, $values);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::insert($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function insertGetId(array $values, $sequence = null)
|
||||
{
|
||||
// Intercept operations on embedded models and delegate logic
|
||||
// to the parent relation instance.
|
||||
if ($relation = $this->model->getParentRelation()) {
|
||||
$relation->performInsert($this->model, $values);
|
||||
|
||||
return $this->model->getKey();
|
||||
}
|
||||
|
||||
return parent::insertGetId($values, $sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// Intercept operations on embedded models and delegate logic
|
||||
// to the parent relation instance.
|
||||
if ($relation = $this->model->getParentRelation()) {
|
||||
$relation->performDelete($this->model);
|
||||
|
||||
return $this->model->getKey();
|
||||
}
|
||||
|
||||
return parent::delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function increment($column, $amount = 1, array $extra = [])
|
||||
{
|
||||
// Intercept operations on embedded models and delegate logic
|
||||
// to the parent relation instance.
|
||||
if ($relation = $this->model->getParentRelation()) {
|
||||
$value = $this->model->{$column};
|
||||
|
||||
// When doing increment and decrements, Eloquent will automatically
|
||||
// sync the original attributes. We need to change the attribute
|
||||
// temporary in order to trigger an update query.
|
||||
$this->model->{$column} = null;
|
||||
|
||||
$this->model->syncOriginalAttribute($column);
|
||||
|
||||
$result = $this->model->update([$column => $value]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return parent::increment($column, $amount, $extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function decrement($column, $amount = 1, array $extra = [])
|
||||
{
|
||||
// Intercept operations on embedded models and delegate logic
|
||||
// to the parent relation instance.
|
||||
if ($relation = $this->model->getParentRelation()) {
|
||||
$value = $this->model->{$column};
|
||||
|
||||
// When doing increment and decrements, Eloquent will automatically
|
||||
// sync the original attributes. We need to change the attribute
|
||||
// temporary in order to trigger an update query.
|
||||
$this->model->{$column} = null;
|
||||
|
||||
$this->model->syncOriginalAttribute($column);
|
||||
|
||||
return $this->model->update([$column => $value]);
|
||||
}
|
||||
|
||||
return parent::decrement($column, $amount, $extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function chunkById($count, callable $callback, $column = '_id', $alias = null)
|
||||
{
|
||||
return parent::chunkById($count, $callback, $column, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function raw($expression = null)
|
||||
{
|
||||
// Get raw results from the query builder.
|
||||
$results = $this->query->raw($expression);
|
||||
|
||||
// Convert MongoCursor results to a collection of models.
|
||||
if ($results instanceof Cursor) {
|
||||
$results = iterator_to_array($results, false);
|
||||
|
||||
return $this->model->hydrate($results);
|
||||
} // Convert Mongo BSONDocument to a single object.
|
||||
elseif ($results instanceof BSONDocument) {
|
||||
$results = $results->getArrayCopy();
|
||||
|
||||
return $this->model->newFromBuilder((array) $results);
|
||||
} // The result is a single object.
|
||||
elseif (is_array($results) && array_key_exists('_id', $results)) {
|
||||
return $this->model->newFromBuilder((array) $results);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the "updated at" column to an array of values.
|
||||
* TODO Remove if https://github.com/laravel/framework/commit/6484744326531829341e1ff886cc9b628b20d73e
|
||||
* wiil be reverted
|
||||
* Issue in laravel frawework https://github.com/laravel/framework/issues/27791
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
protected function addUpdatedAtColumn(array $values)
|
||||
{
|
||||
if (!$this->model->usesTimestamps() || $this->model->getUpdatedAtColumn() === null) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$column = $this->model->getUpdatedAtColumn();
|
||||
$values = array_merge(
|
||||
[$column => $this->model->freshTimestampString()],
|
||||
$values
|
||||
);
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->query->getConnection();
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Eloquent;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Ifornew\Mongodb\Relations\EmbedsMany;
|
||||
use Ifornew\Mongodb\Relations\EmbedsOne;
|
||||
|
||||
trait EmbedsRelations
|
||||
{
|
||||
/**
|
||||
* Define an embedded one-to-many relationship.
|
||||
* @param string $related
|
||||
* @param string $localKey
|
||||
* @param string $foreignKey
|
||||
* @param string $relation
|
||||
* @return \Ifornew\Mongodb\Relations\EmbedsMany
|
||||
*/
|
||||
protected function embedsMany($related, $localKey = null, $foreignKey = null, $relation = null)
|
||||
{
|
||||
// If no relation name was given, we will use this debug backtrace to extract
|
||||
// the calling method's name and use that as the relationship name as most
|
||||
// of the time this will be what we desire to use for the relationships.
|
||||
if ($relation === null) {
|
||||
list(, $caller) = debug_backtrace(false);
|
||||
|
||||
$relation = $caller['function'];
|
||||
}
|
||||
|
||||
if ($localKey === null) {
|
||||
$localKey = $relation;
|
||||
}
|
||||
|
||||
if ($foreignKey === null) {
|
||||
$foreignKey = Str::snake(class_basename($this));
|
||||
}
|
||||
|
||||
$query = $this->newQuery();
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
return new EmbedsMany($query, $this, $instance, $localKey, $foreignKey, $relation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define an embedded one-to-many relationship.
|
||||
* @param string $related
|
||||
* @param string $localKey
|
||||
* @param string $foreignKey
|
||||
* @param string $relation
|
||||
* @return \Ifornew\Mongodb\Relations\EmbedsOne
|
||||
*/
|
||||
protected function embedsOne($related, $localKey = null, $foreignKey = null, $relation = null)
|
||||
{
|
||||
// If no relation name was given, we will use this debug backtrace to extract
|
||||
// the calling method's name and use that as the relationship name as most
|
||||
// of the time this will be what we desire to use for the relationships.
|
||||
if ($relation === null) {
|
||||
list(, $caller) = debug_backtrace(false);
|
||||
|
||||
$relation = $caller['function'];
|
||||
}
|
||||
|
||||
if ($localKey === null) {
|
||||
$localKey = $relation;
|
||||
}
|
||||
|
||||
if ($foreignKey === null) {
|
||||
$foreignKey = Str::snake(class_basename($this));
|
||||
}
|
||||
|
||||
$query = $this->newQuery();
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
return new EmbedsOne($query, $this, $instance, $localKey, $foreignKey, $relation);
|
||||
}
|
||||
}
|
@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Eloquent;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
||||
use Illuminate\Support\Str;
|
||||
use Ifornew\Mongodb\Helpers\EloquentBuilder;
|
||||
use Ifornew\Mongodb\Relations\BelongsTo;
|
||||
use Ifornew\Mongodb\Relations\BelongsToMany;
|
||||
use Ifornew\Mongodb\Relations\HasMany;
|
||||
use Ifornew\Mongodb\Relations\HasOne;
|
||||
use Ifornew\Mongodb\Relations\MorphTo;
|
||||
|
||||
trait HybridRelations
|
||||
{
|
||||
/**
|
||||
* Define a one-to-one relationship.
|
||||
* @param string $related
|
||||
* @param string $foreignKey
|
||||
* @param string $localKey
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function hasOne($related, $foreignKey = null, $localKey = null)
|
||||
{
|
||||
// Check if it is a relation with an original model.
|
||||
if (!is_subclass_of($related, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return parent::hasOne($related, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
$foreignKey = $foreignKey ?: $this->getForeignKey();
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
$localKey = $localKey ?: $this->getKeyName();
|
||||
|
||||
return new HasOne($instance->newQuery(), $this, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a polymorphic one-to-one relationship.
|
||||
* @param string $related
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $id
|
||||
* @param string $localKey
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
|
||||
*/
|
||||
public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
|
||||
{
|
||||
// Check if it is a relation with an original model.
|
||||
if (!is_subclass_of($related, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return parent::morphOne($related, $name, $type, $id, $localKey);
|
||||
}
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
list($type, $id) = $this->getMorphs($name, $type, $id);
|
||||
|
||||
$localKey = $localKey ?: $this->getKeyName();
|
||||
|
||||
return new MorphOne($instance->newQuery(), $this, $type, $id, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a one-to-many relationship.
|
||||
* @param string $related
|
||||
* @param string $foreignKey
|
||||
* @param string $localKey
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function hasMany($related, $foreignKey = null, $localKey = null)
|
||||
{
|
||||
// Check if it is a relation with an original model.
|
||||
if (!is_subclass_of($related, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return parent::hasMany($related, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
$foreignKey = $foreignKey ?: $this->getForeignKey();
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
$localKey = $localKey ?: $this->getKeyName();
|
||||
|
||||
return new HasMany($instance->newQuery(), $this, $foreignKey, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a polymorphic one-to-many relationship.
|
||||
* @param string $related
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $id
|
||||
* @param string $localKey
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
*/
|
||||
public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
|
||||
{
|
||||
// Check if it is a relation with an original model.
|
||||
if (!is_subclass_of($related, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return parent::morphMany($related, $name, $type, $id, $localKey);
|
||||
}
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
// Here we will gather up the morph type and ID for the relationship so that we
|
||||
// can properly query the intermediate table of a relation. Finally, we will
|
||||
// get the table and create the relationship instances for the developers.
|
||||
list($type, $id) = $this->getMorphs($name, $type, $id);
|
||||
|
||||
$table = $instance->getTable();
|
||||
|
||||
$localKey = $localKey ?: $this->getKeyName();
|
||||
|
||||
return new MorphMany($instance->newQuery(), $this, $type, $id, $localKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define an inverse one-to-one or many relationship.
|
||||
* @param string $related
|
||||
* @param string $foreignKey
|
||||
* @param string $otherKey
|
||||
* @param string $relation
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
|
||||
{
|
||||
// If no relation name was given, we will use this debug backtrace to extract
|
||||
// the calling method's name and use that as the relationship name as most
|
||||
// of the time this will be what we desire to use for the relationships.
|
||||
if ($relation === null) {
|
||||
list($current, $caller) = debug_backtrace(false, 2);
|
||||
|
||||
$relation = $caller['function'];
|
||||
}
|
||||
|
||||
// Check if it is a relation with an original model.
|
||||
if (!is_subclass_of($related, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return parent::belongsTo($related, $foreignKey, $otherKey, $relation);
|
||||
}
|
||||
|
||||
// If no foreign key was supplied, we can use a backtrace to guess the proper
|
||||
// foreign key name by using the name of the relationship function, which
|
||||
// when combined with an "_id" should conventionally match the columns.
|
||||
if ($foreignKey === null) {
|
||||
$foreignKey = Str::snake($relation) . '_id';
|
||||
}
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
// Once we have the foreign key names, we'll just create a new Eloquent query
|
||||
// for the related models and returns the relationship instance which will
|
||||
// actually be responsible for retrieving and hydrating every relations.
|
||||
$query = $instance->newQuery();
|
||||
|
||||
$otherKey = $otherKey ?: $instance->getKeyName();
|
||||
|
||||
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a polymorphic, inverse one-to-one or many relationship.
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $id
|
||||
* @param string $ownerKey
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
|
||||
{
|
||||
// If no name is provided, we will use the backtrace to get the function name
|
||||
// since that is most likely the name of the polymorphic interface. We can
|
||||
// use that to get both the class and foreign key that will be utilized.
|
||||
if ($name === null) {
|
||||
list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
$name = Str::snake($caller['function']);
|
||||
}
|
||||
|
||||
list($type, $id) = $this->getMorphs($name, $type, $id);
|
||||
|
||||
// If the type value is null it is probably safe to assume we're eager loading
|
||||
// the relationship. When that is the case we will pass in a dummy query as
|
||||
// there are multiple types in the morph and we can't use single queries.
|
||||
if (($class = $this->$type) === null) {
|
||||
return new MorphTo(
|
||||
$this->newQuery(), $this, $id, null, $type, $name
|
||||
);
|
||||
}
|
||||
|
||||
// If we are not eager loading the relationship we will essentially treat this
|
||||
// as a belongs-to style relationship since morph-to extends that class and
|
||||
// we will pass in the appropriate values so that it behaves as expected.
|
||||
$class = $this->getActualClassNameForMorph($class);
|
||||
|
||||
$instance = new $class;
|
||||
|
||||
return new MorphTo(
|
||||
$instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a many-to-many relationship.
|
||||
* @param string $related
|
||||
* @param string $collection
|
||||
* @param string $foreignKey
|
||||
* @param string $otherKey
|
||||
* @param string $parentKey
|
||||
* @param string $relatedKey
|
||||
* @param string $relation
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function belongsToMany(
|
||||
$related,
|
||||
$collection = null,
|
||||
$foreignKey = null,
|
||||
$otherKey = null,
|
||||
$parentKey = null,
|
||||
$relatedKey = null,
|
||||
$relation = null
|
||||
) {
|
||||
// If no relationship name was passed, we will pull backtraces to get the
|
||||
// name of the calling function. We will use that function name as the
|
||||
// title of this relation since that is a great convention to apply.
|
||||
if ($relation === null) {
|
||||
$relation = $this->guessBelongsToManyRelation();
|
||||
}
|
||||
|
||||
// Check if it is a relation with an original model.
|
||||
if (!is_subclass_of($related, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return parent::belongsToMany(
|
||||
$related,
|
||||
$collection,
|
||||
$foreignKey,
|
||||
$otherKey,
|
||||
$parentKey,
|
||||
$relatedKey,
|
||||
$relation
|
||||
);
|
||||
}
|
||||
|
||||
// First, we'll need to determine the foreign key and "other key" for the
|
||||
// relationship. Once we have determined the keys we'll make the query
|
||||
// instances as well as the relationship instances we need for this.
|
||||
$foreignKey = $foreignKey ?: $this->getForeignKey() . 's';
|
||||
|
||||
$instance = new $related;
|
||||
|
||||
$otherKey = $otherKey ?: $instance->getForeignKey() . 's';
|
||||
|
||||
// If no table name was provided, we can guess it by concatenating the two
|
||||
// models using underscores in alphabetical order. The two model names
|
||||
// are transformed to snake case from their default CamelCase also.
|
||||
if ($collection === null) {
|
||||
$collection = $instance->getTable();
|
||||
}
|
||||
|
||||
// Now we're ready to create a new query builder for the related model and
|
||||
// the relationship instances for the relation. The relations will set
|
||||
// appropriate query constraint and entirely manages the hydrations.
|
||||
$query = $instance->newQuery();
|
||||
|
||||
return new BelongsToMany(
|
||||
$query,
|
||||
$this,
|
||||
$collection,
|
||||
$foreignKey,
|
||||
$otherKey,
|
||||
$parentKey ?: $this->getKeyName(),
|
||||
$relatedKey ?: $instance->getKeyName(),
|
||||
$relation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationship name of the belongs to many.
|
||||
* @return string
|
||||
*/
|
||||
protected function guessBelongsToManyRelation()
|
||||
{
|
||||
if (method_exists($this, 'getBelongsToManyCaller')) {
|
||||
return $this->getBelongsToManyCaller();
|
||||
}
|
||||
|
||||
return parent::guessBelongsToManyRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function newEloquentBuilder($query)
|
||||
{
|
||||
if (is_subclass_of($this, \Ifornew\Mongodb\Eloquent\Model::class)) {
|
||||
return new Builder($query);
|
||||
}
|
||||
|
||||
return new EloquentBuilder($query);
|
||||
}
|
||||
}
|
@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Eloquent;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DateTime;
|
||||
use Illuminate\Contracts\Queue\QueueableCollection;
|
||||
use Illuminate\Contracts\Queue\QueueableEntity;
|
||||
use Illuminate\Database\Eloquent\Model as BaseModel;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Ifornew\Mongodb\Query\Builder as QueryBuilder;
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
|
||||
abstract class Model extends BaseModel
|
||||
{
|
||||
use HybridRelations, EmbedsRelations;
|
||||
|
||||
/**
|
||||
* The collection associated with the model.
|
||||
* @var string
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = '_id';
|
||||
|
||||
/**
|
||||
* The primary key type.
|
||||
* @var string
|
||||
*/
|
||||
protected $keyType = 'string';
|
||||
|
||||
/**
|
||||
* The parent relation instance.
|
||||
* @var Relation
|
||||
*/
|
||||
protected $parentRelation;
|
||||
|
||||
/**
|
||||
* Custom accessor for the model's id.
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdAttribute($value = null)
|
||||
{
|
||||
// If we don't have a value for 'id', we will use the Mongo '_id' value.
|
||||
// This allows us to work with models in a more sql-like way.
|
||||
if (!$value && array_key_exists('_id', $this->attributes)) {
|
||||
$value = $this->attributes['_id'];
|
||||
}
|
||||
|
||||
// Convert ObjectID to string.
|
||||
if ($value instanceof ObjectID) {
|
||||
return (string) $value;
|
||||
} elseif ($value instanceof Binary) {
|
||||
return (string) $value->getData();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQualifiedKeyName()
|
||||
{
|
||||
return $this->getKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function fromDateTime($value)
|
||||
{
|
||||
// If the value is already a UTCDateTime instance, we don't need to parse it.
|
||||
if ($value instanceof UTCDateTime) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Let Eloquent convert the value to a DateTime instance.
|
||||
if (!$value instanceof DateTime) {
|
||||
$value = parent::asDateTime($value);
|
||||
}
|
||||
|
||||
return new UTCDateTime($value->getTimestamp() * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function asDateTime($value)
|
||||
{
|
||||
// Convert UTCDateTime instances.
|
||||
if ($value instanceof UTCDateTime) {
|
||||
return Carbon::createFromTimestamp($value->toDateTime()->getTimestamp());
|
||||
}
|
||||
|
||||
return parent::asDateTime($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getDateFormat()
|
||||
{
|
||||
return $this->dateFormat ?: 'Y-m-d H:i:s';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function freshTimestamp()
|
||||
{
|
||||
return new UTCDateTime(time() * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTable()
|
||||
{
|
||||
return $this->collection ?: parent::getTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getAttribute($key)
|
||||
{
|
||||
if (!$key) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dot notation support.
|
||||
if (Str::contains($key, '.') && Arr::has($this->attributes, $key)) {
|
||||
return $this->getAttributeValue($key);
|
||||
}
|
||||
|
||||
// This checks for embedded relation support.
|
||||
if (method_exists($this, $key) && !method_exists(self::class, $key)) {
|
||||
return $this->getRelationValue($key);
|
||||
}
|
||||
|
||||
return parent::getAttribute($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getAttributeFromArray($key)
|
||||
{
|
||||
// Support keys in dot notation.
|
||||
if (Str::contains($key, '.')) {
|
||||
return Arr::get($this->attributes, $key);
|
||||
}
|
||||
|
||||
return parent::getAttributeFromArray($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setAttribute($key, $value)
|
||||
{
|
||||
// Convert _id to ObjectID.
|
||||
if ($key == '_id' && is_string($value)) {
|
||||
$builder = $this->newBaseQueryBuilder();
|
||||
|
||||
$value = $builder->convertKey($value);
|
||||
} // Support keys in dot notation.
|
||||
elseif (Str::contains($key, '.')) {
|
||||
if (in_array($key, $this->getDates()) && $value) {
|
||||
$value = $this->fromDateTime($value);
|
||||
}
|
||||
|
||||
Arr::set($this->attributes, $key, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::setAttribute($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function attributesToArray()
|
||||
{
|
||||
$attributes = parent::attributesToArray();
|
||||
|
||||
// Because the original Eloquent never returns objects, we convert
|
||||
// MongoDB related objects to a string representation. This kind
|
||||
// of mimics the SQL behaviour so that dates are formatted
|
||||
// nicely when your models are converted to JSON.
|
||||
foreach ($attributes as $key => &$value) {
|
||||
if ($value instanceof ObjectID) {
|
||||
$value = (string) $value;
|
||||
} elseif ($value instanceof Binary) {
|
||||
$value = (string) $value->getData();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert dot-notation dates.
|
||||
foreach ($this->getDates() as $key) {
|
||||
if (Str::contains($key, '.') && Arr::has($attributes, $key)) {
|
||||
Arr::set($attributes, $key, (string) $this->asDateTime(Arr::get($attributes, $key)));
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getCasts()
|
||||
{
|
||||
return $this->casts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function originalIsEquivalent($key, $current)
|
||||
{
|
||||
if (!array_key_exists($key, $this->original)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$original = $this->getOriginal($key);
|
||||
|
||||
if ($current === $original) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (null === $current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isDateAttribute($key)) {
|
||||
$current = $current instanceof UTCDateTime ? $this->asDateTime($current) : $current;
|
||||
$original = $original instanceof UTCDateTime ? $this->asDateTime($original) : $original;
|
||||
|
||||
return $current == $original;
|
||||
}
|
||||
|
||||
if ($this->hasCast($key)) {
|
||||
return $this->castAttribute($key, $current) ===
|
||||
$this->castAttribute($key, $original);
|
||||
}
|
||||
|
||||
return is_numeric($current) && is_numeric($original)
|
||||
&& strcmp((string) $current, (string) $original) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or more fields.
|
||||
* @param mixed $columns
|
||||
* @return int
|
||||
*/
|
||||
public function drop($columns)
|
||||
{
|
||||
$columns = Arr::wrap($columns);
|
||||
|
||||
// Unset attributes
|
||||
foreach ($columns as $column) {
|
||||
$this->__unset($column);
|
||||
}
|
||||
|
||||
// Perform unset only on current document
|
||||
return $this->newQuery()->where($this->getKeyName(), $this->getKey())->unset($columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function push()
|
||||
{
|
||||
if ($parameters = func_get_args()) {
|
||||
$unique = false;
|
||||
|
||||
if (count($parameters) === 3) {
|
||||
list($column, $values, $unique) = $parameters;
|
||||
} else {
|
||||
list($column, $values) = $parameters;
|
||||
}
|
||||
|
||||
// Do batch push by default.
|
||||
$values = Arr::wrap($values);
|
||||
|
||||
$query = $this->setKeysForSaveQuery($this->newQuery());
|
||||
|
||||
$this->pushAttributeValues($column, $values, $unique);
|
||||
|
||||
return $query->push($column, $values, $unique);
|
||||
}
|
||||
|
||||
return parent::push();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or more values from an array.
|
||||
* @param string $column
|
||||
* @param mixed $values
|
||||
* @return mixed
|
||||
*/
|
||||
public function pull($column, $values)
|
||||
{
|
||||
// Do batch pull by default.
|
||||
$values = Arr::wrap($values);
|
||||
|
||||
$query = $this->setKeysForSaveQuery($this->newQuery());
|
||||
|
||||
$this->pullAttributeValues($column, $values);
|
||||
|
||||
return $query->pull($column, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append one or more values to the underlying attribute value and sync with original.
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
* @param bool $unique
|
||||
*/
|
||||
protected function pushAttributeValues($column, array $values, $unique = false)
|
||||
{
|
||||
$current = $this->getAttributeFromArray($column) ?: [];
|
||||
|
||||
foreach ($values as $value) {
|
||||
// Don't add duplicate values when we only want unique values.
|
||||
if ($unique && (!is_array($current) || in_array($value, $current))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$current[] = $value;
|
||||
}
|
||||
|
||||
$this->attributes[$column] = $current;
|
||||
|
||||
$this->syncOriginalAttribute($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or more values to the underlying attribute value and sync with original.
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
*/
|
||||
protected function pullAttributeValues($column, array $values)
|
||||
{
|
||||
$current = $this->getAttributeFromArray($column) ?: [];
|
||||
|
||||
if (is_array($current)) {
|
||||
foreach ($values as $value) {
|
||||
$keys = array_keys($current, $value);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
unset($current[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->attributes[$column] = array_values($current);
|
||||
|
||||
$this->syncOriginalAttribute($column);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getForeignKey()
|
||||
{
|
||||
return Str::snake(class_basename($this)) . '_' . ltrim($this->primaryKey, '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent relation.
|
||||
* @param \Illuminate\Database\Eloquent\Relations\Relation $relation
|
||||
*/
|
||||
public function setParentRelation(Relation $relation)
|
||||
{
|
||||
$this->parentRelation = $relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent relation.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||
*/
|
||||
public function getParentRelation()
|
||||
{
|
||||
return $this->parentRelation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function newEloquentBuilder($query)
|
||||
{
|
||||
return new Builder($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function newBaseQueryBuilder()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
|
||||
return new QueryBuilder($connection, $connection->getPostProcessor());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function removeTableFromKey($key)
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the queueable relationships for the entity.
|
||||
* @return array
|
||||
*/
|
||||
public function getQueueableRelations()
|
||||
{
|
||||
$relations = [];
|
||||
|
||||
foreach ($this->getRelationsWithoutParent() as $key => $relation) {
|
||||
if (method_exists($this, $key)) {
|
||||
$relations[] = $key;
|
||||
}
|
||||
|
||||
if ($relation instanceof QueueableCollection) {
|
||||
foreach ($relation->getQueueableRelations() as $collectionValue) {
|
||||
$relations[] = $key . '.' . $collectionValue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($relation instanceof QueueableEntity) {
|
||||
foreach ($relation->getQueueableRelations() as $entityKey => $entityValue) {
|
||||
$relations[] = $key . '.' . $entityValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($relations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loaded relations for the instance without parent.
|
||||
* @return array
|
||||
*/
|
||||
protected function getRelationsWithoutParent()
|
||||
{
|
||||
$relations = $this->getRelations();
|
||||
|
||||
if ($parentRelation = $this->getParentRelation()) {
|
||||
unset($relations[$parentRelation->getQualifiedForeignKeyName()]);
|
||||
}
|
||||
|
||||
return $relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
// Unset method
|
||||
if ($method == 'unset') {
|
||||
return call_user_func_array([$this, 'drop'], $parameters);
|
||||
}
|
||||
|
||||
return parent::__call($method, $parameters);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Eloquent;
|
||||
|
||||
trait SoftDeletes
|
||||
{
|
||||
use \Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQualifiedDeletedAtColumn()
|
||||
{
|
||||
return $this->getDeletedAtColumn();
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Helpers;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class EloquentBuilder extends Builder
|
||||
{
|
||||
use QueriesRelationships;
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Helpers;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
|
||||
use Ifornew\Mongodb\Eloquent\Model;
|
||||
|
||||
trait QueriesRelationships
|
||||
{
|
||||
/**
|
||||
* Add a relationship count / exists condition to the query.
|
||||
* @param string $relation
|
||||
* @param string $operator
|
||||
* @param int $count
|
||||
* @param string $boolean
|
||||
* @param Closure|null $callback
|
||||
* @return Builder|static
|
||||
*/
|
||||
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
|
||||
{
|
||||
if (strpos($relation, '.') !== false) {
|
||||
return $this->hasNested($relation, $operator, $count, $boolean, $callback);
|
||||
}
|
||||
|
||||
$relation = $this->getRelationWithoutConstraints($relation);
|
||||
|
||||
//修正混合关联关系无法使用 whereHas 的问题
|
||||
if(!($relation instanceof \Ifornew\Mongodb\Relations\BelongsTo ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\BelongsToMany ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\HasMany ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\HasOne ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\MorphTo ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\EmbedsMany ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\EmbedsOne ||
|
||||
$relation instanceof \Ifornew\Mongodb\Relations\EmbedsOneOrMany
|
||||
)){
|
||||
return parent::has($relation, $operator, $count, $boolean, $callback);
|
||||
}
|
||||
|
||||
// If this is a hybrid relation then we can not use a normal whereExists() query that relies on a subquery
|
||||
// We need to use a `whereIn` query
|
||||
if ($this->getModel() instanceof Model || $this->isAcrossConnections($relation)) {
|
||||
return $this->addHybridHas($relation, $operator, $count, $boolean, $callback);
|
||||
}
|
||||
|
||||
// If we only need to check for the existence of the relation, then we can optimize
|
||||
// the subquery to only run a "where exists" clause instead of this full "count"
|
||||
// clause. This will make these queries run much faster compared with a count.
|
||||
$method = $this->canUseExistsForExistenceCheck($operator, $count)
|
||||
? 'getRelationExistenceQuery'
|
||||
: 'getRelationExistenceCountQuery';
|
||||
|
||||
$hasQuery = $relation->{$method}(
|
||||
$relation->getRelated()->newQuery(), $this
|
||||
);
|
||||
|
||||
// Next we will call any given callback as an "anonymous" scope so they can get the
|
||||
// proper logical grouping of the where clauses if needed by this Eloquent query
|
||||
// builder. Then, we will be ready to finalize and return this query instance.
|
||||
if ($callback) {
|
||||
$hasQuery->callScope($callback);
|
||||
}
|
||||
|
||||
return $this->addHasWhere(
|
||||
$hasQuery, $relation, $operator, $count, $boolean
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $relation
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAcrossConnections($relation)
|
||||
{
|
||||
return $relation->getParent()->getConnectionName() !== $relation->getRelated()->getConnectionName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare across databases
|
||||
* @param $relation
|
||||
* @param string $operator
|
||||
* @param int $count
|
||||
* @param string $boolean
|
||||
* @param Closure|null $callback
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function addHybridHas($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
|
||||
{
|
||||
$hasQuery = $relation->getQuery();
|
||||
if ($callback) {
|
||||
$hasQuery->callScope($callback);
|
||||
}
|
||||
|
||||
// If the operator is <, <= or !=, we will use whereNotIn.
|
||||
$not = in_array($operator, ['<', '<=', '!=']);
|
||||
// If we are comparing to 0, we need an additional $not flip.
|
||||
if ($count == 0) {
|
||||
$not = !$not;
|
||||
}
|
||||
|
||||
$relations = $hasQuery->pluck($this->getHasCompareKey($relation));
|
||||
|
||||
$relatedIds = $this->getConstrainedRelatedIds($relations, $operator, $count);
|
||||
|
||||
return $this->whereIn($this->getRelatedConstraintKey($relation), $relatedIds, $boolean, $not);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $relation
|
||||
* @return string
|
||||
*/
|
||||
protected function getHasCompareKey($relation)
|
||||
{
|
||||
if (method_exists($relation, 'getHasCompareKey')) {
|
||||
return $relation->getHasCompareKey();
|
||||
}
|
||||
|
||||
return $relation instanceof HasOneOrMany ? $relation->getForeignKeyName() : $relation->getOwnerKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $relations
|
||||
* @param $operator
|
||||
* @param $count
|
||||
* @return array
|
||||
*/
|
||||
protected function getConstrainedRelatedIds($relations, $operator, $count)
|
||||
{
|
||||
$relationCount = array_count_values(array_map(function ($id) {
|
||||
return (string) $id; // Convert Back ObjectIds to Strings
|
||||
}, is_array($relations) ? $relations : $relations->flatten()->toArray()));
|
||||
// Remove unwanted related objects based on the operator and count.
|
||||
$relationCount = array_filter($relationCount, function ($counted) use ($count, $operator) {
|
||||
// If we are comparing to 0, we always need all results.
|
||||
if ($count == 0) {
|
||||
return true;
|
||||
}
|
||||
switch ($operator) {
|
||||
case '>=':
|
||||
case '<':
|
||||
return $counted >= $count;
|
||||
case '>':
|
||||
case '<=':
|
||||
return $counted > $count;
|
||||
case '=':
|
||||
case '!=':
|
||||
return $counted == $count;
|
||||
}
|
||||
});
|
||||
|
||||
// All related ids.
|
||||
return array_keys($relationCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns key we are constraining this parent model's query with
|
||||
* @param $relation
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getRelatedConstraintKey($relation)
|
||||
{
|
||||
if ($relation instanceof HasOneOrMany) {
|
||||
return $this->model->getKeyName();
|
||||
}
|
||||
|
||||
if ($relation instanceof BelongsTo) {
|
||||
return $relation->getForeignKeyName();
|
||||
}
|
||||
|
||||
if ($relation instanceof BelongsToMany && !$this->isAcrossConnections($relation)) {
|
||||
return $this->model->getKeyName();
|
||||
}
|
||||
|
||||
throw new Exception(class_basename($relation) . ' is not supported for hybrid query constraints.');
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb;
|
||||
|
||||
use DB;
|
||||
use Illuminate\Queue\QueueServiceProvider;
|
||||
use Ifornew\Mongodb\Queue\Failed\MongoFailedJobProvider;
|
||||
|
||||
class MongodbQueueServiceProvider extends QueueServiceProvider
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function registerFailedJobServices()
|
||||
{
|
||||
// Add compatible queue failer if mongodb is configured.
|
||||
if (DB::connection(config('queue.failed.database'))->getDriverName() == 'mongodb') {
|
||||
$this->app->singleton('queue.failer', function ($app) {
|
||||
return new MongoFailedJobProvider($app['db'], config('queue.failed.database'), config('queue.failed.table'));
|
||||
});
|
||||
} else {
|
||||
parent::registerFailedJobServices();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Ifornew\Mongodb\Eloquent\Model;
|
||||
use Ifornew\Mongodb\Queue\MongoConnector;
|
||||
|
||||
class MongodbServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Model::setConnectionResolver($this->app['db']);
|
||||
|
||||
Model::setEventDispatcher($this->app['events']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Add database driver.
|
||||
$this->app->resolving('db', function ($db) {
|
||||
$db->extend('mongodb', function ($config, $name) {
|
||||
$config['name'] = $name;
|
||||
return new Connection($config);
|
||||
});
|
||||
});
|
||||
|
||||
// Add connector for queue support.
|
||||
$this->app->resolving('queue', function ($queue) {
|
||||
$queue->addConnector('mongodb', function () {
|
||||
return new MongoConnector($this->app['db']);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Query;
|
||||
|
||||
use Illuminate\Database\Query\Grammars\Grammar as BaseGrammar;
|
||||
|
||||
class Grammar extends BaseGrammar
|
||||
{
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Query;
|
||||
|
||||
use Illuminate\Database\Query\Processors\Processor as BaseProcessor;
|
||||
|
||||
class Processor extends BaseProcessor
|
||||
{
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Queue\Failed;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Queue\Failed\DatabaseFailedJobProvider;
|
||||
|
||||
class MongoFailedJobProvider extends DatabaseFailedJobProvider
|
||||
{
|
||||
/**
|
||||
* Log a failed job into storage.
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param string $payload
|
||||
* @return void
|
||||
*/
|
||||
public function log($connection, $queue, $payload, $exception)
|
||||
{
|
||||
$failed_at = Carbon::now()->getTimestamp();
|
||||
|
||||
$this->getTable()->insert(compact('connection', 'queue', 'payload', 'failed_at', 'exception'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all of the failed jobs.
|
||||
* @return object[]
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$all = $this->getTable()->orderBy('_id', 'desc')->get()->all();
|
||||
|
||||
$all = array_map(function ($job) {
|
||||
$job['id'] = (string) $job['_id'];
|
||||
return (object) $job;
|
||||
}, $all);
|
||||
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single failed job.
|
||||
* @param mixed $id
|
||||
* @return object
|
||||
*/
|
||||
public function find($id)
|
||||
{
|
||||
$job = $this->getTable()->find($id);
|
||||
|
||||
if (!$job) {
|
||||
return;
|
||||
}
|
||||
|
||||
$job['id'] = (string) $job['_id'];
|
||||
|
||||
return (object) $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single failed job from storage.
|
||||
* @param mixed $id
|
||||
* @return bool
|
||||
*/
|
||||
public function forget($id)
|
||||
{
|
||||
return $this->getTable()->where('_id', $id)->delete() > 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Queue;
|
||||
|
||||
use Illuminate\Database\ConnectionResolverInterface;
|
||||
use Illuminate\Queue\Connectors\ConnectorInterface;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class MongoConnector implements ConnectorInterface
|
||||
{
|
||||
/**
|
||||
* Database connections.
|
||||
* @var \Illuminate\Database\ConnectionResolverInterface
|
||||
*/
|
||||
protected $connections;
|
||||
|
||||
/**
|
||||
* Create a new connector instance.
|
||||
* @param \Illuminate\Database\ConnectionResolverInterface $connections
|
||||
*/
|
||||
public function __construct(ConnectionResolverInterface $connections)
|
||||
{
|
||||
$this->connections = $connections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a queue connection.
|
||||
* @param array $config
|
||||
* @return \Illuminate\Contracts\Queue\Queue
|
||||
*/
|
||||
public function connect(array $config)
|
||||
{
|
||||
return new MongoQueue(
|
||||
$this->connections->connection(Arr::get($config, 'connection')),
|
||||
$config['table'],
|
||||
$config['queue'],
|
||||
Arr::get($config, 'expire', 60)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Queue;
|
||||
|
||||
use Illuminate\Queue\Jobs\DatabaseJob;
|
||||
|
||||
class MongoJob extends DatabaseJob
|
||||
{
|
||||
/**
|
||||
* Indicates if the job has been reserved.
|
||||
* @return bool
|
||||
*/
|
||||
public function isReserved()
|
||||
{
|
||||
return $this->job->reserved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function reservedAt()
|
||||
{
|
||||
return $this->job->reserved_at;
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Queue\DatabaseQueue;
|
||||
use Ifornew\Mongodb\Connection;
|
||||
use MongoDB\Operation\FindOneAndUpdate;
|
||||
|
||||
class MongoQueue extends DatabaseQueue
|
||||
{
|
||||
/**
|
||||
* The expiration time of a job.
|
||||
* @var int|null
|
||||
*/
|
||||
protected $retryAfter = 60;
|
||||
|
||||
/**
|
||||
* The connection name for the queue.
|
||||
* @var string
|
||||
*/
|
||||
protected $connectionName;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct(Connection $database, $table, $default = 'default', $retryAfter = 60)
|
||||
{
|
||||
parent::__construct($database, $table, $default, $retryAfter);
|
||||
$this->retryAfter = $retryAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function pop($queue = null)
|
||||
{
|
||||
$queue = $this->getQueue($queue);
|
||||
|
||||
if ($this->retryAfter !== null) {
|
||||
$this->releaseJobsThatHaveBeenReservedTooLong($queue);
|
||||
}
|
||||
|
||||
if ($job = $this->getNextAvailableJobAndReserve($queue)) {
|
||||
return new MongoJob(
|
||||
$this->container, $this, $job, $this->connectionName, $queue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next available job for the queue and mark it as reserved.
|
||||
* When using multiple daemon queue listeners to process jobs there
|
||||
* is a possibility that multiple processes can end up reading the
|
||||
* same record before one has flagged it as reserved.
|
||||
* This race condition can result in random jobs being run more then
|
||||
* once. To solve this we use findOneAndUpdate to lock the next jobs
|
||||
* record while flagging it as reserved at the same time.
|
||||
* @param string|null $queue
|
||||
* @return \StdClass|null
|
||||
*/
|
||||
protected function getNextAvailableJobAndReserve($queue)
|
||||
{
|
||||
$job = $this->database->getCollection($this->table)->findOneAndUpdate(
|
||||
[
|
||||
'queue' => $this->getQueue($queue),
|
||||
'reserved' => ['$ne' => 1],
|
||||
'available_at' => ['$lte' => Carbon::now()->getTimestamp()],
|
||||
],
|
||||
[
|
||||
'$set' => [
|
||||
'reserved' => 1,
|
||||
'reserved_at' => Carbon::now()->getTimestamp(),
|
||||
],
|
||||
'$inc' => [
|
||||
'attempts' => 1,
|
||||
],
|
||||
],
|
||||
[
|
||||
'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
|
||||
'sort' => ['available_at' => 1],
|
||||
]
|
||||
);
|
||||
|
||||
if ($job) {
|
||||
$job->id = $job->_id;
|
||||
}
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the jobs that have been reserved for too long.
|
||||
* @param string $queue
|
||||
* @return void
|
||||
*/
|
||||
protected function releaseJobsThatHaveBeenReservedTooLong($queue)
|
||||
{
|
||||
$expiration = Carbon::now()->subSeconds($this->retryAfter)->getTimestamp();
|
||||
|
||||
$reserved = $this->database->collection($this->table)
|
||||
->where('queue', $this->getQueue($queue))
|
||||
->whereNotNull('reserved_at')
|
||||
->where('reserved_at', '<=', $expiration)
|
||||
->get();
|
||||
|
||||
foreach ($reserved as $job) {
|
||||
$this->releaseJob($job['_id'], $job['attempts']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the given job ID from reservation.
|
||||
* @param string $id
|
||||
* @param int $attempts
|
||||
* @return void
|
||||
*/
|
||||
protected function releaseJob($id, $attempts)
|
||||
{
|
||||
$this->database->table($this->table)->where('_id', $id)->update([
|
||||
'reserved' => 0,
|
||||
'reserved_at' => null,
|
||||
'attempts' => $attempts,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function deleteReserved($queue, $id)
|
||||
{
|
||||
$this->database->collection($this->table)->where('_id', $id)->delete();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
|
||||
class BelongsTo extends \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
/**
|
||||
* Get the key for comparing against the parent key in "has" query.
|
||||
* @return string
|
||||
*/
|
||||
public function getHasCompareKey()
|
||||
{
|
||||
return $this->getOwnerKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addConstraints()
|
||||
{
|
||||
if (static::$constraints) {
|
||||
// For belongs to relationships, which are essentially the inverse of has one
|
||||
// or has many relationships, we need to actually query on the primary key
|
||||
// of the related models matching on the foreign key that's on a parent.
|
||||
$this->query->where($this->getOwnerKey(), '=', $this->parent->{$this->foreignKey});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addEagerConstraints(array $models)
|
||||
{
|
||||
// We'll grab the primary key name of the related models since it could be set to
|
||||
// a non-standard name and not "id". We will then construct the constraint for
|
||||
// our eagerly loading query so it returns the proper models from execution.
|
||||
$key = $this->getOwnerKey();
|
||||
|
||||
$this->query->whereIn($key, $this->getEagerModelKeys($models));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the owner key with backwards compatible support.
|
||||
* @return string
|
||||
*/
|
||||
public function getOwnerKey()
|
||||
{
|
||||
return property_exists($this, 'ownerKey') ? $this->ownerKey : $this->otherKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMany;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class BelongsToMany extends EloquentBelongsToMany
|
||||
{
|
||||
/**
|
||||
* Get the key for comparing against the parent key in "has" query.
|
||||
* @return string
|
||||
*/
|
||||
public function getHasCompareKey()
|
||||
{
|
||||
return $this->getForeignKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function hydratePivotRelation(array $models)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the select clause for the relation query.
|
||||
* @param array $columns
|
||||
* @return array
|
||||
*/
|
||||
protected function getSelectColumns(array $columns = ['*'])
|
||||
{
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function shouldSelect(array $columns = ['*'])
|
||||
{
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addConstraints()
|
||||
{
|
||||
if (static::$constraints) {
|
||||
$this->setWhere();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the where clause for the relation query.
|
||||
* @return $this
|
||||
*/
|
||||
protected function setWhere()
|
||||
{
|
||||
$foreign = $this->getForeignKey();
|
||||
|
||||
$this->query->where($foreign, '=', $this->parent->getKey());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function save(Model $model, array $joining = [], $touch = true)
|
||||
{
|
||||
$model->save(['touch' => false]);
|
||||
|
||||
$this->attach($model, $joining, $touch);
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function create(array $attributes = [], array $joining = [], $touch = true)
|
||||
{
|
||||
$instance = $this->related->newInstance($attributes);
|
||||
|
||||
// Once we save the related model, we need to attach it to the base model via
|
||||
// through intermediate table so we'll use the existing "attach" method to
|
||||
// accomplish this which will insert the record and any more attributes.
|
||||
$instance->save(['touch' => false]);
|
||||
|
||||
$this->attach($instance, $joining, $touch);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function sync($ids, $detaching = true)
|
||||
{
|
||||
$changes = [
|
||||
'attached' => [],
|
||||
'detached' => [],
|
||||
'updated' => [],
|
||||
];
|
||||
|
||||
if ($ids instanceof Collection) {
|
||||
$ids = $ids->modelKeys();
|
||||
}
|
||||
|
||||
// First we need to attach any of the associated models that are not currently
|
||||
// in this joining table. We'll spin through the given IDs, checking to see
|
||||
// if they exist in the array of current ones, and if not we will insert.
|
||||
$current = $this->parent->{$this->getRelatedKey()} ?: [];
|
||||
|
||||
// See issue #256.
|
||||
if ($current instanceof Collection) {
|
||||
$current = $ids->modelKeys();
|
||||
}
|
||||
|
||||
$records = $this->formatSyncList($ids);
|
||||
|
||||
$current = Arr::wrap($current);
|
||||
|
||||
$detach = array_diff($current, array_keys($records));
|
||||
|
||||
// We need to make sure we pass a clean array, so that it is not interpreted
|
||||
// as an associative array.
|
||||
$detach = array_values($detach);
|
||||
|
||||
// Next, we will take the differences of the currents and given IDs and detach
|
||||
// all of the entities that exist in the "current" array but are not in the
|
||||
// the array of the IDs given to the method which will complete the sync.
|
||||
if ($detaching && count($detach) > 0) {
|
||||
$this->detach($detach);
|
||||
|
||||
$changes['detached'] = (array) array_map(function ($v) {
|
||||
return is_numeric($v) ? (int) $v : (string) $v;
|
||||
}, $detach);
|
||||
}
|
||||
|
||||
// Now we are finally ready to attach the new records. Note that we'll disable
|
||||
// touching until after the entire operation is complete so we don't fire a
|
||||
// ton of touch operations until we are totally done syncing the records.
|
||||
$changes = array_merge(
|
||||
$changes, $this->attachNew($records, $current, false)
|
||||
);
|
||||
|
||||
if (count($changes['attached']) || count($changes['updated'])) {
|
||||
$this->touchIfTouching();
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function updateExistingPivot($id, array $attributes, $touch = true)
|
||||
{
|
||||
// Do nothing, we have no pivot table.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function attach($id, array $attributes = [], $touch = true)
|
||||
{
|
||||
if ($id instanceof Model) {
|
||||
$model = $id;
|
||||
|
||||
$id = $model->getKey();
|
||||
|
||||
// Attach the new parent id to the related model.
|
||||
$model->push($this->foreignPivotKey, $this->parent->getKey(), true);
|
||||
} else {
|
||||
if ($id instanceof Collection) {
|
||||
$id = $id->modelKeys();
|
||||
}
|
||||
|
||||
$query = $this->newRelatedQuery();
|
||||
|
||||
$query->whereIn($this->related->getKeyName(), (array) $id);
|
||||
|
||||
// Attach the new parent id to the related model.
|
||||
$query->push($this->foreignPivotKey, $this->parent->getKey(), true);
|
||||
}
|
||||
|
||||
// Attach the new ids to the parent model.
|
||||
$this->parent->push($this->getRelatedKey(), (array) $id, true);
|
||||
|
||||
if ($touch) {
|
||||
$this->touchIfTouching();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function detach($ids = [], $touch = true)
|
||||
{
|
||||
if ($ids instanceof Model) {
|
||||
$ids = (array) $ids->getKey();
|
||||
}
|
||||
|
||||
$query = $this->newRelatedQuery();
|
||||
|
||||
// If associated IDs were passed to the method we will only delete those
|
||||
// associations, otherwise all of the association ties will be broken.
|
||||
// We'll return the numbers of affected rows when we do the deletes.
|
||||
$ids = (array) $ids;
|
||||
|
||||
// Detach all ids from the parent model.
|
||||
$this->parent->pull($this->getRelatedKey(), $ids);
|
||||
|
||||
// Prepare the query to select all related objects.
|
||||
if (count($ids) > 0) {
|
||||
$query->whereIn($this->related->getKeyName(), $ids);
|
||||
}
|
||||
|
||||
// Remove the relation to the parent.
|
||||
$query->pull($this->foreignPivotKey, $this->parent->getKey());
|
||||
|
||||
if ($touch) {
|
||||
$this->touchIfTouching();
|
||||
}
|
||||
|
||||
return count($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function buildDictionary(Collection $results)
|
||||
{
|
||||
$foreign = $this->foreignPivotKey;
|
||||
|
||||
// First we will build a dictionary of child models keyed by the foreign key
|
||||
// of the relation so that we will easily and quickly match them to their
|
||||
// parents without having a possibly slow inner loops for every models.
|
||||
$dictionary = [];
|
||||
|
||||
foreach ($results as $result) {
|
||||
foreach ($result->$foreign as $item) {
|
||||
$dictionary[$item][] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function newPivotQuery()
|
||||
{
|
||||
return $this->newRelatedQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new query builder for the related model.
|
||||
* @return \Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function newRelatedQuery()
|
||||
{
|
||||
return $this->related->newQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified foreign key for the relation.
|
||||
* @return string
|
||||
*/
|
||||
public function getForeignKey()
|
||||
{
|
||||
return $this->foreignPivotKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQualifiedForeignPivotKeyName()
|
||||
{
|
||||
return $this->foreignPivotKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQualifiedRelatedPivotKeyName()
|
||||
{
|
||||
return $this->relatedPivotKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the sync list so that it is keyed by ID. (Legacy Support)
|
||||
* The original function has been renamed to formatRecordsList since Laravel 5.3
|
||||
* @param array $records
|
||||
* @return array
|
||||
* @deprecated
|
||||
*/
|
||||
protected function formatSyncList(array $records)
|
||||
{
|
||||
$results = [];
|
||||
foreach ($records as $id => $attributes) {
|
||||
if (!is_array($attributes)) {
|
||||
list($id, $attributes) = [$attributes, []];
|
||||
}
|
||||
$results[$id] = $attributes;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the related key with backwards compatible support.
|
||||
* @return string
|
||||
*/
|
||||
public function getRelatedKey()
|
||||
{
|
||||
return property_exists($this, 'relatedPivotKey') ? $this->relatedPivotKey : $this->relatedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,331 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
|
||||
class EmbedsMany extends EmbedsOneOrMany
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function initRelation(array $models, $relation)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$model->setRelation($relation, $this->related->newCollection());
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
return $this->toCollection($this->getEmbedded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new model and attach it to the parent model.
|
||||
* @param Model $model
|
||||
* @return Model|bool
|
||||
*/
|
||||
public function performInsert(Model $model)
|
||||
{
|
||||
// Generate a new key if needed.
|
||||
if ($model->getKeyName() == '_id' && !$model->getKey()) {
|
||||
$model->setAttribute('_id', new ObjectID);
|
||||
}
|
||||
|
||||
// For deeply nested documents, let the parent handle the changes.
|
||||
if ($this->isNested()) {
|
||||
$this->associate($model);
|
||||
return $this->parent->save() ? $model : false;
|
||||
}
|
||||
|
||||
// Push the new model to the database.
|
||||
$result = $this->getBaseQuery()->push($this->localKey, $model->getAttributes(), true);
|
||||
|
||||
// Attach the model to its parent.
|
||||
if ($result) {
|
||||
$this->associate($model);
|
||||
}
|
||||
|
||||
return $result ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an existing model and attach it to the parent model.
|
||||
* @param Model $model
|
||||
* @return Model|bool
|
||||
*/
|
||||
public function performUpdate(Model $model)
|
||||
{
|
||||
// For deeply nested documents, let the parent handle the changes.
|
||||
if ($this->isNested()) {
|
||||
$this->associate($model);
|
||||
|
||||
return $this->parent->save();
|
||||
}
|
||||
|
||||
// Get the correct foreign key value.
|
||||
$foreignKey = $this->getForeignKeyValue($model);
|
||||
|
||||
$values = $this->getUpdateValues($model->getDirty(), $this->localKey . '.$.');
|
||||
|
||||
// Update document in database.
|
||||
$result = $this->getBaseQuery()->where($this->localKey . '.' . $model->getKeyName(), $foreignKey)
|
||||
->update($values);
|
||||
|
||||
// Attach the model to its parent.
|
||||
if ($result) {
|
||||
$this->associate($model);
|
||||
}
|
||||
|
||||
return $result ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing model and detach it from the parent model.
|
||||
* @param Model $model
|
||||
* @return int
|
||||
*/
|
||||
public function performDelete(Model $model)
|
||||
{
|
||||
// For deeply nested documents, let the parent handle the changes.
|
||||
if ($this->isNested()) {
|
||||
$this->dissociate($model);
|
||||
|
||||
return $this->parent->save();
|
||||
}
|
||||
|
||||
// Get the correct foreign key value.
|
||||
$foreignKey = $this->getForeignKeyValue($model);
|
||||
|
||||
$result = $this->getBaseQuery()->pull($this->localKey, [$model->getKeyName() => $foreignKey]);
|
||||
|
||||
if ($result) {
|
||||
$this->dissociate($model);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the model instance to the given parent, without saving it to the database.
|
||||
* @param Model $model
|
||||
* @return Model
|
||||
*/
|
||||
public function associate(Model $model)
|
||||
{
|
||||
if (!$this->contains($model)) {
|
||||
return $this->associateNew($model);
|
||||
}
|
||||
|
||||
return $this->associateExisting($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dissociate the model instance from the given parent, without saving it to the database.
|
||||
* @param mixed $ids
|
||||
* @return int
|
||||
*/
|
||||
public function dissociate($ids = [])
|
||||
{
|
||||
$ids = $this->getIdsArrayFrom($ids);
|
||||
|
||||
$records = $this->getEmbedded();
|
||||
|
||||
$primaryKey = $this->related->getKeyName();
|
||||
|
||||
// Remove the document from the parent model.
|
||||
foreach ($records as $i => $record) {
|
||||
if (in_array($record[$primaryKey], $ids)) {
|
||||
unset($records[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setEmbedded($records);
|
||||
|
||||
// We return the total number of deletes for the operation. The developers
|
||||
// can then check this number as a boolean type value or get this total count
|
||||
// of records deleted for logging, etc.
|
||||
return count($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the embedded models for the given IDs.
|
||||
* @param mixed $ids
|
||||
* @return int
|
||||
*/
|
||||
public function destroy($ids = [])
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
$ids = $this->getIdsArrayFrom($ids);
|
||||
|
||||
// Get all models matching the given ids.
|
||||
$models = $this->getResults()->only($ids);
|
||||
|
||||
// Pull the documents from the database.
|
||||
foreach ($models as $model) {
|
||||
if ($model->delete()) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all embedded models.
|
||||
* @return int
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// Overwrite the local key with an empty array.
|
||||
$result = $this->query->update([$this->localKey => []]);
|
||||
|
||||
if ($result) {
|
||||
$this->setEmbedded([]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy alias.
|
||||
* @param mixed $ids
|
||||
* @return int
|
||||
*/
|
||||
public function detach($ids = [])
|
||||
{
|
||||
return $this->destroy($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save alias.
|
||||
* @param Model $model
|
||||
* @return Model
|
||||
*/
|
||||
public function attach(Model $model)
|
||||
{
|
||||
return $this->save($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new model instance to the given parent, without saving it to the database.
|
||||
* @param Model $model
|
||||
* @return Model
|
||||
*/
|
||||
protected function associateNew($model)
|
||||
{
|
||||
// Create a new key if needed.
|
||||
if ($model->getKeyName() === '_id' && !$model->getAttribute('_id')) {
|
||||
$model->setAttribute('_id', new ObjectID);
|
||||
}
|
||||
|
||||
$records = $this->getEmbedded();
|
||||
|
||||
// Add the new model to the embedded documents.
|
||||
$records[] = $model->getAttributes();
|
||||
|
||||
return $this->setEmbedded($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate an existing model instance to the given parent, without saving it to the database.
|
||||
* @param Model $model
|
||||
* @return Model
|
||||
*/
|
||||
protected function associateExisting($model)
|
||||
{
|
||||
// Get existing embedded documents.
|
||||
$records = $this->getEmbedded();
|
||||
|
||||
$primaryKey = $this->related->getKeyName();
|
||||
|
||||
$key = $model->getKey();
|
||||
|
||||
// Replace the document in the parent model.
|
||||
foreach ($records as &$record) {
|
||||
if ($record[$primaryKey] == $key) {
|
||||
$record = $model->getAttributes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->setEmbedded($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a paginator for the "select" statement.
|
||||
* @param int $perPage
|
||||
* @return \Illuminate\Pagination\AbstractPaginator
|
||||
*/
|
||||
public function paginate($perPage = null)
|
||||
{
|
||||
$page = Paginator::resolveCurrentPage();
|
||||
$perPage = $perPage ?: $this->related->getPerPage();
|
||||
|
||||
$results = $this->getEmbedded();
|
||||
|
||||
$total = count($results);
|
||||
|
||||
$start = ($page - 1) * $perPage;
|
||||
$sliced = array_slice($results, $start, $perPage);
|
||||
|
||||
return new LengthAwarePaginator($sliced, $total, $perPage, $page, [
|
||||
'path' => Paginator::resolveCurrentPath(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getEmbedded()
|
||||
{
|
||||
return parent::getEmbedded() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function setEmbedded($models)
|
||||
{
|
||||
if (!is_array($models)) {
|
||||
$models = [$models];
|
||||
}
|
||||
|
||||
return parent::setEmbedded(array_values($models));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
if (method_exists(Collection::class, $method)) {
|
||||
return call_user_func_array([$this->getResults(), $method], $parameters);
|
||||
}
|
||||
|
||||
return parent::__call($method, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
|
||||
class EmbedsOne extends EmbedsOneOrMany
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function initRelation(array $models, $relation)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$model->setRelation($relation, null);
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
return $this->toModel($this->getEmbedded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new model and attach it to the parent model.
|
||||
* @param Model $model
|
||||
* @return Model|bool
|
||||
*/
|
||||
public function performInsert(Model $model)
|
||||
{
|
||||
// Generate a new key if needed.
|
||||
if ($model->getKeyName() == '_id' && !$model->getKey()) {
|
||||
$model->setAttribute('_id', new ObjectID);
|
||||
}
|
||||
|
||||
// For deeply nested documents, let the parent handle the changes.
|
||||
if ($this->isNested()) {
|
||||
$this->associate($model);
|
||||
return $this->parent->save() ? $model : false;
|
||||
}
|
||||
|
||||
$result = $this->getBaseQuery()->update([$this->localKey => $model->getAttributes()]);
|
||||
|
||||
// Attach the model to its parent.
|
||||
if ($result) {
|
||||
$this->associate($model);
|
||||
}
|
||||
|
||||
return $result ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an existing model and attach it to the parent model.
|
||||
* @param Model $model
|
||||
* @return Model|bool
|
||||
*/
|
||||
public function performUpdate(Model $model)
|
||||
{
|
||||
if ($this->isNested()) {
|
||||
$this->associate($model);
|
||||
|
||||
return $this->parent->save();
|
||||
}
|
||||
|
||||
$values = $this->getUpdateValues($model->getDirty(), $this->localKey . '.');
|
||||
|
||||
$result = $this->getBaseQuery()->update($values);
|
||||
|
||||
// Attach the model to its parent.
|
||||
if ($result) {
|
||||
$this->associate($model);
|
||||
}
|
||||
|
||||
return $result ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing model and detach it from the parent model.
|
||||
* @return int
|
||||
*/
|
||||
public function performDelete()
|
||||
{
|
||||
// For deeply nested documents, let the parent handle the changes.
|
||||
if ($this->isNested()) {
|
||||
$this->dissociate();
|
||||
return $this->parent->save();
|
||||
}
|
||||
|
||||
// Overwrite the local key with an empty array.
|
||||
$result = $this->getBaseQuery()->update([$this->localKey => null]);
|
||||
|
||||
// Detach the model from its parent.
|
||||
if ($result) {
|
||||
$this->dissociate();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the model to its parent.
|
||||
* @param Model $model
|
||||
* @return Model
|
||||
*/
|
||||
public function associate(Model $model)
|
||||
{
|
||||
return $this->setEmbedded($model->getAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach the model from its parent.
|
||||
* @return Model
|
||||
*/
|
||||
public function dissociate()
|
||||
{
|
||||
return $this->setEmbedded(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all embedded models.
|
||||
* @return int
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return $this->performDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Ifornew\Mongodb\Eloquent\Model;
|
||||
|
||||
abstract class EmbedsOneOrMany extends Relation
|
||||
{
|
||||
/**
|
||||
* The local key of the parent model.
|
||||
* @var string
|
||||
*/
|
||||
protected $localKey;
|
||||
|
||||
/**
|
||||
* The foreign key of the parent model.
|
||||
* @var string
|
||||
*/
|
||||
protected $foreignKey;
|
||||
|
||||
/**
|
||||
* The "name" of the relationship.
|
||||
* @var string
|
||||
*/
|
||||
protected $relation;
|
||||
|
||||
/**
|
||||
* Create a new embeds many relationship instance.
|
||||
* @param Builder $query
|
||||
* @param Model $parent
|
||||
* @param Model $related
|
||||
* @param string $localKey
|
||||
* @param string $foreignKey
|
||||
* @param string $relation
|
||||
*/
|
||||
public function __construct(Builder $query, Model $parent, Model $related, $localKey, $foreignKey, $relation)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->parent = $parent;
|
||||
$this->related = $related;
|
||||
$this->localKey = $localKey;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->relation = $relation;
|
||||
|
||||
// If this is a nested relation, we need to get the parent query instead.
|
||||
if ($parentRelation = $this->getParentRelation()) {
|
||||
$this->query = $parentRelation->getQuery();
|
||||
}
|
||||
|
||||
$this->addConstraints();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addConstraints()
|
||||
{
|
||||
if (static::$constraints) {
|
||||
$this->query->where($this->getQualifiedParentKeyName(), '=', $this->getParentKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addEagerConstraints(array $models)
|
||||
{
|
||||
// There are no eager loading constraints.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function match(array $models, Collection $results, $relation)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$results = $model->$relation()->getResults();
|
||||
|
||||
$model->setParentRelation($this);
|
||||
|
||||
$model->setRelation($relation, $results);
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand to get the results of the relationship.
|
||||
* @param array $columns
|
||||
* @return Collection
|
||||
*/
|
||||
public function get($columns = ['*'])
|
||||
{
|
||||
return $this->getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of embedded models.
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->getEmbedded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a model instance to the parent model.
|
||||
* @param Model $model
|
||||
* @return Model|bool
|
||||
*/
|
||||
public function save(Model $model)
|
||||
{
|
||||
$model->setParentRelation($this);
|
||||
|
||||
return $model->save() ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a collection of models to the parent instance.
|
||||
* @param Collection|array $models
|
||||
* @return Collection|array
|
||||
*/
|
||||
public function saveMany($models)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$this->save($model);
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the related model.
|
||||
* @param array $attributes
|
||||
* @return Model
|
||||
*/
|
||||
public function create(array $attributes = [])
|
||||
{
|
||||
// Here we will set the raw attributes to avoid hitting the "fill" method so
|
||||
// that we do not have to worry about a mass accessor rules blocking sets
|
||||
// on the models. Otherwise, some of these attributes will not get set.
|
||||
$instance = $this->related->newInstance($attributes);
|
||||
|
||||
$instance->setParentRelation($this);
|
||||
|
||||
$instance->save();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of new instances of the related model.
|
||||
* @param array $records
|
||||
* @return array
|
||||
*/
|
||||
public function createMany(array $records)
|
||||
{
|
||||
$instances = [];
|
||||
|
||||
foreach ($records as $record) {
|
||||
$instances[] = $this->create($record);
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform single ID, single Model or array of Models into an array of IDs.
|
||||
* @param mixed $ids
|
||||
* @return array
|
||||
*/
|
||||
protected function getIdsArrayFrom($ids)
|
||||
{
|
||||
if ($ids instanceof \Illuminate\Support\Collection) {
|
||||
$ids = $ids->all();
|
||||
}
|
||||
|
||||
if (!is_array($ids)) {
|
||||
$ids = [$ids];
|
||||
}
|
||||
|
||||
foreach ($ids as &$id) {
|
||||
if ($id instanceof Model) {
|
||||
$id = $id->getKey();
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getEmbedded()
|
||||
{
|
||||
// Get raw attributes to skip relations and accessors.
|
||||
$attributes = $this->parent->getAttributes();
|
||||
|
||||
// Get embedded models form parent attributes.
|
||||
$embedded = isset($attributes[$this->localKey]) ? (array) $attributes[$this->localKey] : null;
|
||||
|
||||
return $embedded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function setEmbedded($records)
|
||||
{
|
||||
// Assign models to parent attributes array.
|
||||
$attributes = $this->parent->getAttributes();
|
||||
$attributes[$this->localKey] = $records;
|
||||
|
||||
// Set raw attributes to skip mutators.
|
||||
$this->parent->setRawAttributes($attributes);
|
||||
|
||||
// Set the relation on the parent.
|
||||
return $this->parent->setRelation($this->relation, $records === null ? null : $this->getResults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the foreign key value for the relation.
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getForeignKeyValue($id)
|
||||
{
|
||||
if ($id instanceof Model) {
|
||||
$id = $id->getKey();
|
||||
}
|
||||
|
||||
// Convert the id to MongoId if necessary.
|
||||
return $this->getBaseQuery()->convertKey($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array of records to a Collection.
|
||||
* @param array $records
|
||||
* @return Collection
|
||||
*/
|
||||
protected function toCollection(array $records = [])
|
||||
{
|
||||
$models = [];
|
||||
|
||||
foreach ($records as $attributes) {
|
||||
$models[] = $this->toModel($attributes);
|
||||
}
|
||||
|
||||
if (count($models) > 0) {
|
||||
$models = $this->eagerLoadRelations($models);
|
||||
}
|
||||
|
||||
return $this->related->newCollection($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a related model instanced.
|
||||
* @param array $attributes
|
||||
* @return Model
|
||||
*/
|
||||
protected function toModel($attributes = [])
|
||||
{
|
||||
if ($attributes === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connection = $this->related->getConnection();
|
||||
|
||||
$model = $this->related->newFromBuilder(
|
||||
(array) $attributes,
|
||||
$connection ? $connection->getName() : null
|
||||
);
|
||||
|
||||
$model->setParentRelation($this);
|
||||
|
||||
$model->setRelation($this->foreignKey, $this->parent);
|
||||
|
||||
// If you remove this, you will get segmentation faults!
|
||||
$model->setHidden(array_merge($model->getHidden(), [$this->foreignKey]));
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relation instance of the parent.
|
||||
* @return Relation
|
||||
*/
|
||||
protected function getParentRelation()
|
||||
{
|
||||
return $this->parent->getParentRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
// Because we are sharing this relation instance to models, we need
|
||||
// to make sure we use separate query instances.
|
||||
return clone $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getBaseQuery()
|
||||
{
|
||||
// Because we are sharing this relation instance to models, we need
|
||||
// to make sure we use separate query instances.
|
||||
return clone $this->query->getQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this relation is nested in another relation.
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNested()
|
||||
{
|
||||
return $this->getParentRelation() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified local key name.
|
||||
* @param string $glue
|
||||
* @return string
|
||||
*/
|
||||
protected function getPathHierarchy($glue = '.')
|
||||
{
|
||||
if ($parentRelation = $this->getParentRelation()) {
|
||||
return $parentRelation->getPathHierarchy($glue) . $glue . $this->localKey;
|
||||
}
|
||||
|
||||
return $this->localKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQualifiedParentKeyName()
|
||||
{
|
||||
if ($parentRelation = $this->getParentRelation()) {
|
||||
return $parentRelation->getPathHierarchy() . '.' . $this->parent->getKeyName();
|
||||
}
|
||||
|
||||
return $this->parent->getKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary key value of the parent.
|
||||
* @return string
|
||||
*/
|
||||
protected function getParentKey()
|
||||
{
|
||||
return $this->parent->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return update values
|
||||
* @param $array
|
||||
* @param string $prepend
|
||||
* @return array
|
||||
*/
|
||||
public static function getUpdateValues($array, $prepend = '')
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
$results[$prepend . $key] = $value;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the foreign key for the relationship.
|
||||
* @return string
|
||||
*/
|
||||
public function getQualifiedForeignKeyName()
|
||||
{
|
||||
return $this->foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany as EloquentHasMany;
|
||||
|
||||
class HasMany extends EloquentHasMany
|
||||
{
|
||||
/**
|
||||
* Get the plain foreign key.
|
||||
* @return string
|
||||
*/
|
||||
public function getForeignKeyName()
|
||||
{
|
||||
return $this->foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plain foreign key.
|
||||
* @return string
|
||||
*/
|
||||
public function getPlainForeignKey()
|
||||
{
|
||||
return $this->getForeignKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key for comparing against the parent key in "has" query.
|
||||
* @return string
|
||||
*/
|
||||
public function getHasCompareKey()
|
||||
{
|
||||
return $this->getForeignKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
|
||||
{
|
||||
$foreignKey = $this->getHasCompareKey();
|
||||
|
||||
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the constraints for a relationship count query.
|
||||
* @param Builder $query
|
||||
* @param Builder $parent
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRelationCountQuery(Builder $query, Builder $parent)
|
||||
{
|
||||
$foreignKey = $this->getHasCompareKey();
|
||||
|
||||
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the constraints for a relationship query.
|
||||
* @param Builder $query
|
||||
* @param Builder $parent
|
||||
* @param array|mixed $columns
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRelationQuery(Builder $query, Builder $parent, $columns = ['*'])
|
||||
{
|
||||
$query->select($columns);
|
||||
|
||||
$key = $this->wrap($this->getQualifiedParentKeyName());
|
||||
|
||||
return $query->where($this->getHasCompareKey(), 'exists', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne as EloquentHasOne;
|
||||
|
||||
class HasOne extends EloquentHasOne
|
||||
{
|
||||
/**
|
||||
* Get the key for comparing against the parent key in "has" query.
|
||||
* @return string
|
||||
*/
|
||||
public function getForeignKeyName()
|
||||
{
|
||||
return $this->foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key for comparing against the parent key in "has" query.
|
||||
* @return string
|
||||
*/
|
||||
public function getHasCompareKey()
|
||||
{
|
||||
return $this->getForeignKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plain foreign key.
|
||||
* @return string
|
||||
*/
|
||||
public function getPlainForeignKey()
|
||||
{
|
||||
return $this->getForeignKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
|
||||
{
|
||||
$foreignKey = $this->getForeignKeyName();
|
||||
|
||||
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the constraints for a relationship count query.
|
||||
* @param Builder $query
|
||||
* @param Builder $parent
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRelationCountQuery(Builder $query, Builder $parent)
|
||||
{
|
||||
$foreignKey = $this->getForeignKeyName();
|
||||
|
||||
return $query->select($foreignKey)->where($foreignKey, 'exists', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the constraints for a relationship query.
|
||||
* @param Builder $query
|
||||
* @param Builder $parent
|
||||
* @param array|mixed $columns
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRelationQuery(Builder $query, Builder $parent, $columns = ['*'])
|
||||
{
|
||||
$query->select($columns);
|
||||
|
||||
$key = $this->wrap($this->getQualifiedParentKeyName());
|
||||
|
||||
return $query->where($this->getForeignKeyName(), 'exists', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Relations;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model as EloquentModel;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo as EloquentMorphTo;
|
||||
|
||||
class MorphTo extends EloquentMorphTo
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addConstraints()
|
||||
{
|
||||
if (static::$constraints) {
|
||||
// For belongs to relationships, which are essentially the inverse of has one
|
||||
// or has many relationships, we need to actually query on the primary key
|
||||
// of the related models matching on the foreign key that's on a parent.
|
||||
$this->query->where($this->getOwnerKey(), '=', $this->parent->{$this->foreignKey});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getResultsByType($type)
|
||||
{
|
||||
$instance = $this->createModelByType($type);
|
||||
|
||||
$key = $instance->getKeyName();
|
||||
|
||||
$query = $instance->newQuery();
|
||||
|
||||
return $query->whereIn($key, $this->gatherKeysByType($type))->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the owner key with backwards compatible support.
|
||||
* @return string
|
||||
*/
|
||||
public function getOwnerKey()
|
||||
{
|
||||
return property_exists($this, 'ownerKey') ? $this->ownerKey : $this->otherKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "where in" method for eager loading.
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function whereInMethod(EloquentModel $model, $key)
|
||||
{
|
||||
return 'whereIn';
|
||||
}
|
||||
}
|
@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Schema;
|
||||
|
||||
use Illuminate\Database\Connection;
|
||||
|
||||
class Blueprint extends \Illuminate\Database\Schema\Blueprint
|
||||
{
|
||||
/**
|
||||
* The MongoConnection object for this blueprint.
|
||||
* @var \Ifornew\Mongodb\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The MongoCollection object for this blueprint.
|
||||
* @var \Ifornew\Mongodb\Collection|\MongoDB\Collection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* Fluent columns.
|
||||
* @var array
|
||||
*/
|
||||
protected $columns = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct(Connection $connection, $collection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
$this->collection = $this->connection->getCollection($collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function index($columns = null, $name = null, $algorithm = null, $options = [])
|
||||
{
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
// Columns are passed as a default array.
|
||||
if (is_array($columns) && is_int(key($columns))) {
|
||||
// Transform the columns to the required array format.
|
||||
$transform = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$transform[$column] = 1;
|
||||
}
|
||||
|
||||
$columns = $transform;
|
||||
}
|
||||
|
||||
if ($name !== null) {
|
||||
$options['name'] = $name;
|
||||
}
|
||||
|
||||
$this->collection->createIndex($columns, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function primary($columns = null, $name = null, $algorithm = null, $options = [])
|
||||
{
|
||||
return $this->unique($columns, $name, $algorithm, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function dropIndex($indexOrColumns = null)
|
||||
{
|
||||
$indexOrColumns = $this->transformColumns($indexOrColumns);
|
||||
|
||||
$this->collection->dropIndex($indexOrColumns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the given index should be dropped, but do not fail if it didn't exist.
|
||||
*
|
||||
* @param string|array $indexOrColumns
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function dropIndexIfExists($indexOrColumns = null)
|
||||
{
|
||||
if ($this->hasIndex($indexOrColumns)) {
|
||||
$this->dropIndex($indexOrColumns);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given index exists.
|
||||
*
|
||||
* @param string|array $indexOrColumns
|
||||
* @return bool
|
||||
*/
|
||||
public function hasIndex($indexOrColumns = null)
|
||||
{
|
||||
$indexOrColumns = $this->transformColumns($indexOrColumns);
|
||||
foreach ($this->collection->listIndexes() as $index) {
|
||||
if (is_array($indexOrColumns) && in_array($index->getName(), $indexOrColumns)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_string($indexOrColumns) && $index->getName() == $indexOrColumns) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $indexOrColumns
|
||||
* @return string
|
||||
*/
|
||||
protected function transformColumns($indexOrColumns)
|
||||
{
|
||||
if (is_array($indexOrColumns)) {
|
||||
$indexOrColumns = $this->fluent($indexOrColumns);
|
||||
|
||||
// Transform the columns to the index name.
|
||||
$transform = [];
|
||||
|
||||
foreach ($indexOrColumns as $column) {
|
||||
$transform[$column] = $column . '_1';
|
||||
}
|
||||
|
||||
$indexOrColumns = implode('_', $transform);
|
||||
}
|
||||
return $indexOrColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function unique($columns = null, $name = null, $algorithm = null, $options = [])
|
||||
{
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
$options['unique'] = true;
|
||||
|
||||
$this->index($columns, $name, $algorithm, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a non blocking index for the collection.
|
||||
* @param string|array $columns
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function background($columns = null)
|
||||
{
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
$this->index($columns, null, null, ['background' => true]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a sparse index for the collection.
|
||||
* @param string|array $columns
|
||||
* @param array $options
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function sparse($columns = null, $options = [])
|
||||
{
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
$options['sparse'] = true;
|
||||
|
||||
$this->index($columns, null, null, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a geospatial index for the collection.
|
||||
* @param string|array $columns
|
||||
* @param string $index
|
||||
* @param array $options
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function geospatial($columns = null, $index = '2d', $options = [])
|
||||
{
|
||||
if ($index == '2d' || $index == '2dsphere') {
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
$columns = array_flip($columns);
|
||||
|
||||
foreach ($columns as $column => $value) {
|
||||
$columns[$column] = $index;
|
||||
}
|
||||
|
||||
$this->index($columns, null, null, $options);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of seconds after wich a document should be considered expired based,
|
||||
* on the given single-field index containing a date.
|
||||
* @param string|array $columns
|
||||
* @param int $seconds
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function expire($columns, $seconds)
|
||||
{
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
$this->index($columns, null, null, ['expireAfterSeconds' => $seconds]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$collection = $this->collection->getCollectionName();
|
||||
|
||||
$db = $this->connection->getMongoDB();
|
||||
|
||||
// Ensure the collection is created.
|
||||
$db->createCollection($collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function drop()
|
||||
{
|
||||
$this->collection->drop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function addColumn($type, $name, array $parameters = [])
|
||||
{
|
||||
$this->fluent($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a sparse and unique index for the collection.
|
||||
* @param string|array $columns
|
||||
* @param array $options
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function sparse_and_unique($columns = null, $options = [])
|
||||
{
|
||||
$columns = $this->fluent($columns);
|
||||
|
||||
$options['sparse'] = true;
|
||||
$options['unique'] = true;
|
||||
|
||||
$this->index($columns, null, null, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow fluent columns.
|
||||
* @param string|array $columns
|
||||
* @return string|array
|
||||
*/
|
||||
protected function fluent($columns = null)
|
||||
{
|
||||
if ($columns === null) {
|
||||
return $this->columns;
|
||||
} elseif (is_string($columns)) {
|
||||
return $this->columns = [$columns];
|
||||
} else {
|
||||
return $this->columns = $columns;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the use of unsupported schema methods.
|
||||
* @param $method
|
||||
* @param $args
|
||||
* @return Blueprint
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
// Dummy.
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Schema;
|
||||
|
||||
use Closure;
|
||||
use Ifornew\Mongodb\Connection;
|
||||
|
||||
class Builder extends \Illuminate\Database\Schema\Builder
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function hasColumn($table, $column)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function hasColumns($table, array $columns)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given collection exists.
|
||||
* @param string $collection
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCollection($collection)
|
||||
{
|
||||
$db = $this->connection->getMongoDB();
|
||||
|
||||
foreach ($db->listCollections() as $collectionFromMongo) {
|
||||
if ($collectionFromMongo->getName() == $collection) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function hasTable($collection)
|
||||
{
|
||||
return $this->hasCollection($collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a collection on the schema.
|
||||
* @param string $collection
|
||||
* @param Closure $callback
|
||||
* @return bool
|
||||
*/
|
||||
public function collection($collection, Closure $callback)
|
||||
{
|
||||
$blueprint = $this->createBlueprint($collection);
|
||||
|
||||
if ($callback) {
|
||||
$callback($blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function table($collection, Closure $callback)
|
||||
{
|
||||
return $this->collection($collection, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function create($collection, Closure $callback = null, array $options = [])
|
||||
{
|
||||
$blueprint = $this->createBlueprint($collection);
|
||||
|
||||
$blueprint->create($options);
|
||||
|
||||
if ($callback) {
|
||||
$callback($blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function dropIfExists($collection)
|
||||
{
|
||||
if ($this->hasCollection($collection)) {
|
||||
return $this->drop($collection);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function drop($collection)
|
||||
{
|
||||
$blueprint = $this->createBlueprint($collection);
|
||||
|
||||
return $blueprint->drop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function dropAllTables()
|
||||
{
|
||||
foreach ($this->getAllCollections() as $collection) {
|
||||
$this->drop($collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function createBlueprint($collection, Closure $callback = null)
|
||||
{
|
||||
return new Blueprint($this->connection, $collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the collections names for the database.
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllCollections()
|
||||
{
|
||||
$collections = [];
|
||||
foreach ($this->connection->getMongoDB()->listCollections() as $collection) {
|
||||
$collections[] = $collection->getName();
|
||||
}
|
||||
|
||||
return $collections;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Schema;
|
||||
|
||||
use Illuminate\Database\Schema\Grammars\Grammar as BaseGrammar;
|
||||
|
||||
class Grammar extends BaseGrammar
|
||||
{
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb;
|
||||
|
||||
use Ifornew\Mongodb\Query\Builder;
|
||||
|
||||
trait TransactionTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \MongoDB\Driver\Session
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* @description: 创建事务
|
||||
*
|
||||
* @param array $options
|
||||
* @date 2019-07-22
|
||||
*/
|
||||
public function beginTransaction(array $options = [])
|
||||
{
|
||||
if (!$this->getSession()) {
|
||||
$this->session = $this->getMongoClient()->startSession();
|
||||
$this->session->startTransaction($options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 提交
|
||||
*
|
||||
* @date 2019-07-22
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if ($this->getSession()) {
|
||||
$this->session->commitTransaction();
|
||||
$this->clearSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 回滚
|
||||
*
|
||||
* @param $toLevel
|
||||
* @date 2019-07-22
|
||||
*/
|
||||
public function rollBack($toLevel = null)
|
||||
{
|
||||
if ($this->getSession()) {
|
||||
$this->session->abortTransaction();
|
||||
$this->clearSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 清理session
|
||||
*
|
||||
* @date 2019-07-22
|
||||
*/
|
||||
protected function clearSession()
|
||||
{
|
||||
$this->session = null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $collection
|
||||
* @return Builder|\Ifornew\Mongodb\Query\Builder
|
||||
* @date 2019-07-22
|
||||
*/
|
||||
public function collection($collection)
|
||||
{
|
||||
$query = new Query\Builder($this, $this->getPostProcessor());
|
||||
|
||||
return $query->from($collection);
|
||||
}
|
||||
|
||||
public function getSession()
|
||||
{
|
||||
return $this->session;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Validation;
|
||||
|
||||
class DatabasePresenceVerifier extends \Illuminate\Validation\DatabasePresenceVerifier
|
||||
{
|
||||
/**
|
||||
* Count the number of objects in a collection having the given value.
|
||||
* @param string $collection
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
* @param int $excludeId
|
||||
* @param string $idColumn
|
||||
* @param array $extra
|
||||
* @return int
|
||||
*/
|
||||
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = [])
|
||||
{
|
||||
$query = $this->table($collection)->where($column, 'regex', "/$value/i");
|
||||
|
||||
if ($excludeId !== null && $excludeId != 'NULL') {
|
||||
$query->where($idColumn ?: 'id', '<>', $excludeId);
|
||||
}
|
||||
|
||||
foreach ($extra as $key => $extraValue) {
|
||||
$this->addWhere($query, $key, $extraValue);
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of objects in a collection with the given values.
|
||||
* @param string $collection
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
* @param array $extra
|
||||
* @return int
|
||||
*/
|
||||
public function getMultiCount($collection, $column, array $values, array $extra = [])
|
||||
{
|
||||
// Generates a regex like '/(a|b|c)/i' which can query multiple values
|
||||
$regex = '/(' . implode('|', $values) . ')/i';
|
||||
|
||||
$query = $this->table($collection)->where($column, 'regex', $regex);
|
||||
|
||||
foreach ($extra as $key => $extraValue) {
|
||||
$this->addWhere($query, $key, $extraValue);
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Ifornew\Mongodb\Validation;
|
||||
|
||||
use Illuminate\Validation\ValidationServiceProvider as BaseProvider;
|
||||
|
||||
class ValidationServiceProvider extends BaseProvider
|
||||
{
|
||||
protected function registerPresenceVerifier()
|
||||
{
|
||||
$this->app->singleton('validation.presence', function ($app) {
|
||||
return new DatabasePresenceVerifier($app['db']);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue