Commit f5c230f8 authored by Jens Segers's avatar Jens Segers

Merge master

parents 72c64442 57c0d851
Laravel MongoDB
===============
[![Latest Stable Version](https://poser.pugx.org/jenssegers/mongodb/v/stable.png)](https://packagist.org/packages/jenssegers/mongodb) [![Total Downloads](https://poser.pugx.org/jenssegers/mongodb/downloads.png)](https://packagist.org/packages/jenssegers/mongodb) [![Build Status](https://travis-ci.org/jenssegers/Laravel-MongoDB.png?branch=master)](https://travis-ci.org/jenssegers/Laravel-MongoDB) [![Coverage Status](https://coveralls.io/repos/jenssegers/Laravel-MongoDB/badge.png?branch=master)](https://coveralls.io/r/jenssegers/Laravel-MongoDB?branch=master)
[![Latest Stable Version](http://img.shields.io/github/release/jenssegers/laravel-mongodb.svg)](https://packagist.org/packages/jenssegers/mongodb) [![Total Downloads](http://img.shields.io/packagist/dm/jenssegers/mongodb.svg)](https://packagist.org/packages/jenssegers/mongodb) [![Build Status](http://img.shields.io/travis/jenssegers/laravel-mongodb.svg)](https://travis-ci.org/jenssegers/laravel-mongodb) [![Coverage Status](http://img.shields.io/coveralls/jenssegers/laravel-mongodb.svg)](https://coveralls.io/r/jenssegers/laravel-mongodb?branch=master)
An Eloquent model and Query builder with support for MongoDB, inspired by LMongo, but using the original Laravel methods. *This library extends the original Laravel classes, so it uses exactly the same methods.*
......@@ -136,6 +136,11 @@ If you want to use Laravel's native Auth functionality, register this included s
This service provider will slightly modify the internal DatabaseReminderRepository to add support for MongoDB based password reminders. If you don't use password reminders, you don't have to register this service provider and everything else should work just fine.
Sentry
------
If yo want to use this library with [Sentry](https://cartalyst.com/manual/sentry), then check out https://github.com/jenssegers/Laravel-MongoDB-Sentry
Sessions
--------
......@@ -423,6 +428,33 @@ Again, you may override the conventional local key by passing a second argument
return $this->embedsMany('Book', 'local_key');
### EmbedsOne Relations
There is also an EmbedsOne relation, which works similar to the EmbedsMany relation, but only stores one embedded model.
use Jenssegers\Mongodb\Model as Eloquent;
class Book extends Eloquent {
public function author()
{
return $this->embedsOne('Author');
}
}
Now we can access the book's author through the dynamic property:
$author = Book::first()->author;
Inserting and updating embedded documents works just like the `embedsMany` relation:
$author = new Author(array('name' => 'John Doe'));
$book = Books::first();
$author = $user->author()->save($author);
### MySQL Relations
If you're using a hybrid MongoDB and SQL setup, you're in luck! The model will automatically return a MongoDB- or SQL-relation based on the type of the related model. Of course, if you want this functionality to work both ways, your SQL-models will need to extend `Jenssegers\Eloquent\Model`. Note that this functionality only works for hasOne, hasMany and belongsTo relations.
......
......@@ -5,12 +5,12 @@
"authors": [
{
"name": "Jens Segers",
"email": "hello@jenssegers.be"
"homepage": "http://jenssegers.be"
}
],
"license" : "MIT",
"require": {
"php": ">=5.3.0",
"php": ">=5.4.0",
"illuminate/support": "4.2.x",
"illuminate/database": "4.2.x",
"illuminate/events": "4.2.x"
......@@ -25,5 +25,8 @@
"Jenssegers\\Eloquent": "src/"
}
},
"minimum-stability": "dev"
"suggest": {
"jenssegers/mongodb-session": "Add MongoDB session support to Laravel-MongoDB",
"jenssegers/mongodb-sentry": "Add Sentry support to Laravel-MongoDB"
}
}
......@@ -34,6 +34,7 @@
</testsuite>
<testsuite name="relations">
<directory>tests/RelationsTest.php</directory>
<directory>tests/EmbeddedRelationsTest.php</directory>
</testsuite>
<testsuite name="mysqlrelations">
<directory>tests/RelationsTest.php</directory>
......
<?php namespace Jenssegers\Eloquent;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Jenssegers\Mongodb\Relations\HasOne;
use Jenssegers\Mongodb\Relations\HasMany;
use Jenssegers\Mongodb\Relations\BelongsTo;
use Jenssegers\Mongodb\Relations\BelongsToMany;
use Jenssegers\Mongodb\Relations\MorphTo;
......@@ -134,12 +134,6 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model {
*/
public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
{
// Check if it is a relation with an original model.
if (!is_subclass_of($related, 'Jenssegers\Mongodb\Model'))
{
return parent::belongsTo($related, $foreignKey, $otherKey, $relation);
}
// 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 relatinoships.
......@@ -149,6 +143,12 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model {
$relation = $caller['function'];
}
// Check if it is a relation with an original model.
if (!is_subclass_of($related, 'Jenssegers\Mongodb\Model'))
{
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
......@@ -227,12 +227,6 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model {
*/
public function belongsToMany($related, $collection = null, $foreignKey = null, $otherKey = null, $relation = null)
{
// Check if it is a relation with an original model.
if (!is_subclass_of($related, 'Jenssegers\Mongodb\Model'))
{
return parent::belongsToMany($related, $collection, $foreignKey, $otherKey, $relation);
}
// 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.
......@@ -240,6 +234,12 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model {
{
$relation = $this->getBelongsToManyCaller();
}
// Check if it is a relation with an original model.
if (!is_subclass_of($related, 'Jenssegers\Mongodb\Model'))
{
return parent::belongsToMany($related, $collection, $foreignKey, $otherKey, $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
......
......@@ -180,7 +180,7 @@ class Connection extends \Illuminate\Database\Connection {
*/
public function getDriverName()
{
return '';
return 'mongodb';
}
/**
......
<?php namespace Jenssegers\Mongodb\Eloquent;
use MongoCursor;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
class Builder extends \Illuminate\Database\Eloquent\Builder {
class Builder extends EloquentBuilder {
/**
* The methods that should be returned from query builder.
......@@ -14,6 +16,56 @@ class Builder extends \Illuminate\Database\Eloquent\Builder {
'count', 'min', 'max', 'avg', 'sum', 'exists', 'push', 'pull'
);
/**
* Add the "has" condition where clause to the query.
*
* @param \Illuminate\Database\Eloquent\Builder $hasQuery
* @param \Illuminate\Database\Eloquent\Relations\Relation $relation
* @param string $operator
* @param int $count
* @param string $boolean
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function addHasWhere(EloquentBuilder $hasQuery, Relation $relation, $operator, $count, $boolean)
{
$query = $hasQuery->getQuery();
// Get the number of related objects for each possible parent.
$relationCount = array_count_values($query->lists($relation->getHasCompareKey()));
// 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;
}
});
// If the operator is <, <= or !=, we will use whereNotIn.
$not = in_array($operator, array('<', '<=', '!='));
// If we are comparing to 0, we need an additional $not flip.
if ($count == 0) $not = !$not;
// All related ids.
$relatedIds = array_keys($relationCount);
// Add whereIn to the query.
return $this->whereIn($this->model->getKeyName(), $relatedIds, $boolean, $not);
}
/**
* Create a raw database expression.
*
......
......@@ -5,6 +5,7 @@ use Jenssegers\Mongodb\DatabaseManager as Resolver;
use Jenssegers\Mongodb\Eloquent\Builder;
use Jenssegers\Mongodb\Query\Builder as QueryBuilder;
use Jenssegers\Mongodb\Relations\EmbedsMany;
use Jenssegers\Mongodb\Relations\EmbedsOne;
use Carbon\Carbon;
use DateTime;
......@@ -84,6 +85,42 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
return new EmbedsMany($query, $this, $instance, $localKey, $foreignKey, $relation);
}
/**
* Define an embedded one-to-many relationship.
*
* @param string $related
* @param string $collection
* @return \Illuminate\Database\Eloquent\Relations\EmbedsMany
*/
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 relatinoships.
if (is_null($relation))
{
list(, $caller) = debug_backtrace(false);
$relation = $caller['function'];
}
if (is_null($localKey))
{
$localKey = '_' . $relation;
}
if (is_null($foreignKey))
{
$foreignKey = snake_case(class_basename($this));
}
$query = $this->newQuery();
$instance = new $related;
return new EmbedsOne($query, $this, $instance, $localKey, $foreignKey, $relation);
}
/**
* Convert a DateTime to a storable MongoDate object.
*
......@@ -92,19 +129,19 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
*/
public function fromDateTime($value)
{
// Convert DateTime to MongoDate
if ($value instanceof DateTime)
// If the value is already a MongoDate instance, we don't need to parse it.
if ($value instanceof MongoDate)
{
$value = new MongoDate($value->getTimestamp());
return $value;
}
// Convert timestamp to MongoDate
elseif (is_numeric($value))
// Let Eloquent convert the value to a DateTime instance.
if ( ! $value instanceof DateTime)
{
$value = new MongoDate($value);
$value = parent::asDateTime($value);
}
return $value;
return new MongoDate($value->getTimestamp());
}
/**
......@@ -115,25 +152,23 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
*/
protected function asDateTime($value)
{
// Convert timestamp
if (is_numeric($value))
{
return Carbon::createFromTimestamp($value);
}
// Convert string
if (is_string($value))
{
return new Carbon($value);
}
// Convert MongoDate
// Convert MongoDate instances.
if ($value instanceof MongoDate)
{
return Carbon::createFromTimestamp($value->sec);
}
return Carbon::instance($value);
return parent::asDateTime($value);
}
/**
* Get the format for database stored dates.
*
* @return string
*/
protected function getDateFormat()
{
return 'Y-m-d H:i:s';
}
/**
......@@ -159,27 +194,23 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
}
/**
* Set the array of model attributes. No checking is done.
* Get an attribute from the model.
*
* @param array $attributes
* @param bool $sync
* @return void
* @param string $key
* @return mixed
*/
public function setRawAttributes(array $attributes, $sync = false)
public function getAttribute($key)
{
foreach($attributes as $key => &$value)
$attribute = parent::getAttribute($key);
// If the attribute is a MongoId object, return it as a string.
// This is makes Eloquent relations a lot easier.
if ($attribute instanceof MongoId)
{
/**
* MongoIds are converted to string to make it easier to pass
* the id to other instances or relations.
*/
if ($value instanceof MongoId)
{
$value = (string) $value;
}
return (string) $attribute;
}
parent::setRawAttributes($attributes, $sync);
return $attribute;
}
/**
......@@ -191,16 +222,20 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
{
$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 &$value)
{
/**
* Here we convert MongoDate instances to string. This mimics
* original SQL behaviour so that dates are formatted nicely
* when your models are converted to JSON.
*/
if ($value instanceof MongoDate)
if ($value instanceof MongoId)
{
$value = (string) $value;
}
else if ($value instanceof MongoDate)
{
$value = $this->asDateTime($value)->format('Y-m-d H:i:s');
$value = $this->asDateTime($value)->format($this->getDateFormat());
}
}
......@@ -213,7 +248,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
* @param mixed $columns
* @return int
*/
public function dropColumn($columns)
public function drop($columns)
{
if (!is_array($columns)) $columns = array($columns);
......@@ -224,7 +259,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
}
// Perform unset only on current document
return $query = $this->newQuery()->where($this->getKeyName(), $this->getKey())->unset($columns);
return $this->newQuery()->where($this->getKeyName(), $this->getKey())->unset($columns);
}
/**
......@@ -279,7 +314,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
// Unset method
if ($method == 'unset')
{
return call_user_func_array(array($this, 'dropColumn'), $parameters);
return call_user_func_array(array($this, 'drop'), $parameters);
}
return parent::__call($method, $parameters);
......
<?php namespace Jenssegers\Mongodb\Query;
use MongoID;
use MongoId;
use MongoRegex;
use MongoDate;
use DateTime;
......@@ -27,7 +27,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
'=', '<', '>', '<=', '>=', '<>', '!=',
'like', 'not like', 'between', 'ilike',
'&', '|', '^', '<<', '>>',
'exists', 'type', 'mod', 'where', 'all', 'size', 'regex',
'exists', 'type', 'mod', 'where', 'all', 'size', 'regex', 'elemmatch'
);
/**
......@@ -59,7 +59,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
/**
* Execute a query for a single record by ID.
*
* @param int $id
* @param mixed $id
* @param array $columns
* @return mixed
*/
......@@ -377,8 +377,8 @@ class Builder extends \Illuminate\Database\Query\Builder {
$sequence = '_id';
}
// Return id as a string
return (string) $values[$sequence];
// Return id
return $values[$sequence];
}
}
......@@ -585,7 +585,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param mixed $columns
* @return int
*/
public function dropColumn($columns)
public function drop($columns)
{
if (!is_array($columns)) $columns = array($columns);
......@@ -667,23 +667,30 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/
protected function compileWheres()
{
if (!$this->wheres) return array();
// The wheres to compile.
$wheres = $this->wheres ?: array();
// The new list of compiled wheres
$wheres = array();
// We will add all compiled wheres to this array.
$compiled = array();
foreach ($this->wheres as $i => &$where)
foreach ($wheres as $i => &$where)
{
// Make sure the operator is in lowercase
// Make sure the operator is in lowercase.
if (isset($where['operator']))
{
$where['operator'] = strtolower($where['operator']);
// Fix elemMatch.
if ($where['operator'] == 'elemmatch')
{
$where['operator'] = 'elemMatch';
}
}
// Convert id's
// Convert id's.
if (isset($where['column']) && $where['column'] == '_id')
{
// Multiple values
// Multiple values.
if (isset($where['values']))
{
foreach ($where['values'] as &$value)
......@@ -691,48 +698,57 @@ class Builder extends \Illuminate\Database\Query\Builder {
$value = $this->convertKey($value);
}
}
// Single value
elseif (isset($where['value']))
// Single value.
else if (isset($where['value']))
{
$where['value'] = $this->convertKey($where['value']);
}
}
// Convert dates
// Convert DateTime values to MongoDate.
if (isset($where['value']) && $where['value'] instanceof DateTime)
{
$where['value'] = new MongoDate($where['value']->getTimestamp());
}
// First item of chain
if ($i == 0 && count($this->wheres) > 1 && $where['boolean'] == 'and')
// The next item in a "chain" of wheres devices the boolean of the
// first item. So if we see that there are multiple wheres, we will
// use the operator of the next where.
if ($i == 0 and count($wheres) > 1 and $where['boolean'] == 'and')
{
// Copy over boolean value of next item in chain
$where['boolean'] = $this->wheres[$i+1]['boolean'];
$where['boolean'] = $wheres[$i+1]['boolean'];
}
// Delegate
// We use different methods to compile different wheres.
$method = "compileWhere{$where['type']}";
$compiled = $this->{$method}($where);
$result = $this->{$method}($where);
// Check for or
// Wrap the where with an $or operator.
if ($where['boolean'] == 'or')
{
$compiled = array('$or' => array($compiled));
$result = array('$or' => array($result));
}
// If there are multiple wheres, we will wrap it with $and. This is needed
// to make nested wheres work.
else if (count($wheres) > 1)
{
$result = array('$and' => array($result));
}
// Merge compiled where
$wheres = array_merge_recursive($wheres, $compiled);
// Merge the compiled where with the others.
$compiled = array_merge_recursive($compiled, $result);
}
return $wheres;
return $compiled;
}
protected function compileWhereBasic($where)
{
extract($where);
// Replace like with MongoRegex
// Replace like with a MongoRegex instance.
if ($operator == 'like')
{
$operator = '=';
......@@ -745,7 +761,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
$value = new MongoRegex("/$regex/i");
}
if (!isset($operator) || $operator == '=')
if ( ! isset($operator) or $operator == '=')
{
$query = array($column => $value);
}
......@@ -846,7 +862,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
{
if ($method == 'unset')
{
return call_user_func_array(array($this, 'dropColumn'), $parameters);
return call_user_func_array(array($this, 'drop'), $parameters);
}
return parent::__call($method, $parameters);
......
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Collection;
use Jenssegers\Mongodb\Model;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMany;
class BelongsToMany extends EloquentBelongsToMany {
......@@ -36,27 +36,25 @@ class BelongsToMany extends EloquentBelongsToMany {
{
if (static::$constraints)
{
$this->query->where($this->foreignKey, $this->parent->getKey());
$this->query->where($this->getForeignKey(), '=', $this->parent->getKey());
}
}
/**
* Sync the intermediate tables with a list of IDs.
* Sync the intermediate tables with a list of IDs or collection of models.
*
* @param array $ids
* @param bool $detaching
* @return void
*/
public function sync(array $ids, $detaching = true)
public function sync($ids, $detaching = true)
{
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->otherKey};
// Check if the current array exists or not on the parent model and create it
// if it does not exist
if (is_null($current)) $current = array();
$current = $this->parent->{$this->otherKey} ?: array();
$records = $this->formatSyncList($ids);
......@@ -133,27 +131,6 @@ class BelongsToMany extends EloquentBelongsToMany {
if ($touch) $this->touchIfTouching();
}
/**
* Create an array of records to insert into the pivot table.
*
* @param array $ids
* @return void
*/
protected function createAttachRecords($ids, array $attributes)
{
$records = array();
// To create the attachment records, we will simply spin through the IDs given
// and create a new record to insert for each ID. Each ID may actually be a
// key in the array, with extra attributes to be placed in other columns.
foreach ($ids as $key => $value)
{
$records[] = $this->attacher($key, $value, $attributes, false);
}
return $records;
}
/**
* Detach models from the relationship.
*
......@@ -165,6 +142,8 @@ class BelongsToMany extends EloquentBelongsToMany {
{
if ($ids instanceof Model) $ids = (array) $ids->getKey();
$query = $this->getNewRelatedQuery();
// 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.
......@@ -176,9 +155,6 @@ class BelongsToMany extends EloquentBelongsToMany {
$this->parent->pull($this->otherKey, $id);
}
// Get a new related query.
$query = $this->getNewRelatedQuery();
// Prepare the query to select all related objects.
if (count($ids) > 0)
{
......@@ -238,14 +214,4 @@ class BelongsToMany extends EloquentBelongsToMany {
{
return $this->foreignKey;
}
/**
* Get the fully qualified "other key" for the relation.
*
* @return string
*/
public function getOtherKey()
{
return $this->otherKey;
}
}
......@@ -4,293 +4,247 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Collection;
use MongoId;
class EmbedsMany extends Relation {
/**
* The local key of the parent model.
*
* @var string
*/
protected $localKey;
/**
* The foreign key of the parent model.
*
* @var string
*/
protected $foreignKey;
class EmbedsMany extends EmbedsOneOrMany {
/**
* The "name" of the relationship.
* Get the results of the relationship.
*
* @var string
* @return Illuminate\Database\Eloquent\Collection
*/
protected $relation;
public function getResults()
{
return $this->toCollection($this->getEmbedded());
}
/**
* Create a new embeds many relationship instance.
* Simulate order by method.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $localKey
* @param string $foreignKey
* @param string $relation
* @return void
* @param string $column
* @param string $direction
* @return Illuminate\Database\Eloquent\Collection
*/
public function __construct(Builder $query, Model $parent, Model $related, $localKey, $foreignKey, $relation)
public function orderBy($column, $direction = 'asc')
{
$this->query = $query;
$this->parent = $parent;
$this->related = $related;
$this->localKey = $localKey;
$this->foreignKey = $foreignKey;
$this->relation = $relation;
$this->addConstraints();
$descending = strtolower($direction) == 'desc';
return $this->getResults()->sortBy($column, SORT_REGULAR, $descending);
}
/**
* Set the base constraints on the relation query.
* Associate the model instance to the given parent, without saving it to the database.
*
* @return void
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function addConstraints()
public function associate(Model $model)
{
if (static::$constraints)
if ( ! $this->contains($model))
{
$this->query->where($this->parent->getKeyName(), '=', $this->parent->getKey());
return $this->associateNew($model);
}
else
{
return $this->associateExisting($model);
}
}
/**
* Set the constraints for an eager load of the relation.
* Destroy the embedded models for the given IDs.
*
* @param array $models
* @return void
* @param mixed $ids
* @return int
*/
public function addEagerConstraints(array $models)
public function destroy($ids = array())
{
// There are no eager loading constraints.
}
$ids = $this->getIdsArrayFrom($ids);
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return void
*/
public function initRelation(array $models, $relation)
{
// Get all models matching the given ids.
$models = $this->get()->only($ids);
// Pull the documents from the database.
foreach ($models as $model)
{
$model->setRelation($relation, $this->related->newCollection());
$this->performDelete($model);
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
* Dissociate the model instance from the given parent, without saving it to the database.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
* @param mixed $ids
* @return int
*/
public function match(array $models, Collection $results, $relation)
public function dissociate($ids = array())
{
foreach ($models as $model)
{
$results = $this->getEmbeddedRecords($model);
$ids = $this->getIdsArrayFrom($ids);
$records = $this->getEmbedded();
$primaryKey = $this->related->getKeyName();
$model->setRelation($relation, $this->toCollection($results));
// Remove the document from the parent model.
foreach ($records as $i => $record)
{
if (in_array($record[$primaryKey], $ids))
{
unset($records[$i]);
}
}
return $models;
$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);
}
/**
* Get the results of the relationship.
* Delete all embedded models.
*
* @return Illuminate\Database\Eloquent\Collection
* @return int
*/
public function getResults()
public function delete()
{
return $this->toCollection($this->getEmbeddedRecords());
}
// Overwrite the local key with an empty array.
$result = $this->query->update(array($this->localKey => array()));
/**
* Shorthand to get the results of the relationship.
*
* @return Illuminate\Database\Eloquent\Collection
*/
public function get()
{
return $this->getResults();
// If the update query was successful, we will remove the embedded records
// of the parent instance.
if ($result)
{
$count = $this->count();
$this->setEmbedded(array());
// Return the number of deleted embedded records.
return $count;
}
return $result;
}
/**
* Get the results with given ids.
* Destroy alias.
*
* @param array $ids
* @return \Illuminate\Database\Eloquent\Collection
* @param mixed $ids
* @return int
*/
public function find(array $ids)
public function detach($ids = array())
{
$documents = $this->getEmbeddedRecords();
$primaryKey = $this->related->getKeyName();
$documents = array_filter($documents, function ($document) use ($primaryKey, $ids)
{
return in_array($document[$primaryKey], $ids);
});
return $this->toCollection($documents);
return $this->destroy($ids);
}
/**
* Attach a model instance to the parent model.
* Save alias.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model)
public function attach(Model $model)
{
if ($this->fireModelEvent($model, 'saving') === false) return false;
$this->updateTimestamps($model);
// Insert a new document.
if ( ! $model->exists)
{
$result = $this->performInsert($model);
}
// Update an existing document.
else
{
$result = $this->performUpdate($model);
}
if ($result)
{
$this->fireModelEvent($result, 'saved', false);
return $result;
}
return false;
return $this->save($model);
}
/**
* Attach a model instance to the parent model without persistence.
* Save a new model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function associate(Model $model)
protected function performInsert(Model $model)
{
// Insert the related model in the parent instance
if ( ! $model->exists)
// Create a new key if needed.
if ( ! $model->getAttribute('_id'))
{
return $this->associateNew($model);
$model->setAttribute('_id', new MongoId);
}
// Update the related model in the parent instance
else
{
return $this->associateExisting($model);
}
// Push the new model to the database.
$result = $this->query->push($this->localKey, $model->getAttributes(), true);
// Associate the new model to the parent.
if ($result) $this->associateNew($model);
return $result ? $model : false;
}
/**
* Perform a model insert operation.
* Save an existing model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
* @return Model|bool
*/
protected function performInsert(Model $model)
protected function performUpdate(Model $model)
{
if ($this->fireModelEvent($model, 'creating') === false) return false;
// Insert the related model in the parent instance
$this->associateNew($model);
// Get the correct foreign key value.
$id = $this->getForeignKeyValue($model);
// Push the document to the database.
$result = $this->query->push($this->localKey, $model->getAttributes(), true);
// Update document in database.
$result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $id)
->update(array($this->localKey . '.$' => $model->getAttributes()));
if ($result)
{
$this->fireModelEvent($model, 'created', false);
return $model;
}
// Update the related model in the parent instance
if ($result) $this->associateExisting($model);
return false;
return $result ? $model : false;
}
/**
* Perform a model update operation.
* Remove an existing model and detach it from the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return Model|bool
* @return bool
*/
protected function performUpdate(Model $model)
protected function performDelete(Model $model)
{
if ($this->fireModelEvent($model, 'updating') === false) return false;
// Update the related model in the parent instance
$this->associateExisting($model);
if ($this->fireModelEvent($model, 'deleting') === false) return false;
// Get the correct foreign key value.
$id = $this->getForeignKeyValue($model->getKey());
$id = $this->getForeignKeyValue($model);
// Update document in database.
$result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $id)
->update(array($this->localKey . '.$' => $model->getAttributes()));
$result = $this->query->pull($this->localKey, array($model->getKeyName() => $id));
if ($result)
{
$this->fireModelEvent($model, 'updated', false);
return $model;
$this->fireModelEvent($model, 'deleted', false);
// Update the related model in the parent instance
$this->dissociate($model);
return true;
}
return false;
}
/**
* Attach a new model without persistence
* Associate a new model instance to the given parent, without saving it to the database.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
protected function associateNew($model)
{
// Create a new key.
// Create a new key if needed.
if ( ! $model->getAttribute('_id'))
{
$model->setAttribute('_id', new MongoId);
}
$documents = $this->getEmbeddedRecords();
$records = $this->getEmbedded();
// Add the document to the parent model.
$documents[] = $model->getAttributes();
$this->setEmbeddedRecords($documents);
$records[] = $model->getAttributes();
// Mark the model as existing.
$model->exists = true;
$this->setEmbedded($records);
return $model;
}
/**
* Update an existing model without persistence
* Associate an existing model instance to the given parent, without saving it to the database.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
......@@ -298,298 +252,65 @@ class EmbedsMany extends Relation {
protected function associateExisting($model)
{
// Get existing embedded documents.
$documents = $this->getEmbeddedRecords();
$records = $this->getEmbedded();
$primaryKey = $this->related->getKeyName();
$key = $model->getKey();
// Replace the document in the parent model.
foreach ($documents as &$document)
foreach ($records as &$record)
{
if ($document[$primaryKey] == $key)
if ($record[$primaryKey] == $key)
{
$document = $model->getAttributes();
$record = $model->getAttributes();
break;
}
}
$this->setEmbeddedRecords($documents);
$this->setEmbedded($records);
return $model;
}
/**
* Attach an array of models to the parent instance.
* Get the embedded records array.
*
* @param array $models
* @return array
*/
public function saveMany(array $models)
{
array_walk($models, array($this, 'save'));
return $models;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function create(array $attributes)
protected function getEmbedded()
{
// 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();
$instance->setRawAttributes($attributes);
return $this->save($instance);
return parent::getEmbedded() ?: array();
}
/**
* Create an array of new instances of the related model.
*
* @param array $records
* @return array
*/
public function createMany(array $records)
{
$instances = array_map(array($this, 'create'), $records);
return $instances;
}
/**
* Destroy the embedded models for the given IDs.
*
* @param mixed $ids
* @return int
*/
public function destroy($ids = array())
{
$ids = $this->getIdsArrayFrom($ids);
$models = $this->find($ids);
$ids = array();
$primaryKey = $this->related->getKeyName();
// Pull the documents from the database.
foreach ($models as $model)
{
if ($this->fireModelEvent($model, 'deleting') === false) continue;
$id = $model->getKey();
$this->query->pull($this->localKey, array($primaryKey => $this->getForeignKeyValue($id)));
$ids[] = $id;
$this->fireModelEvent($model, 'deleted', false);
}
return $this->dissociate($ids);
}
/**
* Dissociate the embedded models for the given IDs without persistence.
*
* @param mixed $ids
* @return int
*/
public function dissociate($ids = array())
{
$ids = $this->getIdsArrayFrom($ids);
$primaryKey = $this->related->getKeyName();
// Get existing embedded documents.
$documents = $this->getEmbeddedRecords();
// Remove the document from the parent model.
foreach ($documents as $i => $document)
{
if (in_array($document[$primaryKey], $ids))
{
unset($documents[$i]);
}
}
$this->setEmbeddedRecords($documents);
// 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);
}
/**
* Transform single ID, single Model or array of Models into an array of IDs
*
* @param mixed $ids
* @return array
*/
protected function getIdsArrayFrom($ids)
{
if (! is_array($ids)) $ids = array($ids);
foreach ($ids as &$id)
{
if ($id instanceof Model) $id = $id->getKey();
}
return $ids;
}
/**
* Delete alias.
*
* @param mixed $ids
* @return int
*/
public function detach($ids = array())
{
return $this->destroy($ids);
}
/**
* Save alias.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function attach(Model $model)
{
return $this->save($model);
}
/**
* Convert an array of embedded documents to a Collection.
*
* @param array $results
* @return Illuminate\Database\Eloquent\Collection
*/
protected function toCollection(array $results = array())
{
$models = array();
// Wrap documents in model objects.
foreach ($results as $model)
{
if ( ! $model instanceof Model)
{
$model = $this->related->newFromBuilder($model);
}
// Attatch the parent relation to the embedded model.
$model->setRelation($this->foreignKey, $this->parent);
$model->setHidden(array_merge($model->getHidden(), array($this->foreignKey)));
$models[] = $model;
}
if (count($models) > 0)
{
$models = $this->eagerLoadRelations($models);
}
return $this->related->newCollection($models);
}
/**
* Get the embedded documents array.
*
* @return array
*/
protected function getEmbeddedRecords(Model $model = null)
{
if (is_null($model))
{
$model = $this->parent;
}
// Get raw attributes to skip relations and accessors.
$attributes = $model->getAttributes();
return isset($attributes[$this->localKey]) ? $attributes[$this->localKey] : array();
}
/**
* Set the embedded documents array.
* Set the embedded records array.
*
* @param array $models
* @return void
*/
protected function setEmbeddedRecords(array $models)
protected function setEmbedded($models)
{
$attributes = $this->parent->getAttributes();
$attributes[$this->localKey] = array_values($models);
if (! is_array($models)) $models = array($models);
// Set raw attributes to skip mutators.
$this->parent->setRawAttributes($attributes);
// Set the relation on the parent.
$this->parent->setRelation($this->relation, $this->getResults());
return parent::setEmbedded(array_values($models));
}
/**
* Update the creation and update timestamps.
* Handle dynamic method calls to the relationship.
*
* @return void
* @param string $method
* @param array $parameters
* @return mixed
*/
protected function updateTimestamps(Model $model)
public function __call($method, $parameters)
{
$time = $model->freshTimestamp();
if ( ! $model->isDirty(Model::UPDATED_AT))
{
$model->setUpdatedAt($time);
}
if ( ! $model->exists && ! $model->isDirty(Model::CREATED_AT))
// Collection methods
if (method_exists('Illuminate\Database\Eloquent\Collection', $method))
{
$model->setCreatedAt($time);
return call_user_func_array(array($this->getResults(), $method), $parameters);
}
}
/**
* Get the foreign key value for the relation.
*
* @param mixed $id
* @return mixed
*/
protected function getForeignKeyValue($id)
{
// Convert the id to MongoId if necessary.
return $this->getBaseQuery()->convertKey($id);
}
/**
* Fire the given event for the given model.
*
* @param string $event
* @param bool $halt
* @return mixed
*/
protected function fireModelEvent(Model $model, $event, $halt = true)
{
$dispatcher = $model->getEventDispatcher();
if ( is_null($dispatcher)) return true;
// We will append the names of the class to the event to distinguish it from
// other model events that are fired, allowing us to listen on each model
// event set individually instead of catching event for all the models.
$event = "eloquent.{$event}: ".get_class($model);
$method = $halt ? 'until' : 'fire';
return $dispatcher->$method($event, $model);
return parent::__call($method, $parameters);
}
}
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Collection;
use MongoId;
class EmbedsOne extends EmbedsOneOrMany {
/**
* Get the results of the relationship.
*
* @return Illuminate\Database\Eloquent\Collection
*/
public function getResults()
{
return $this->toModel($this->getEmbedded());
}
/**
* Check if a model is already embedded.
*
* @param mixed $key
* @return bool
*/
public function contains($key)
{
if ($key instanceof Model) $key = $key->getKey();
$embedded = $this->getEmbedded();
$primaryKey = $this->related->getKeyName();
return ($embedded and $embedded[$primaryKey] == $key);
}
/**
* Associate the model instance to the given parent, without saving it to the database.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function associate(Model $model)
{
// Create a new key if needed.
if ( ! $model->getAttribute('_id'))
{
$model->setAttribute('_id', new MongoId);
}
$this->setEmbedded($model->getAttributes());
return $model;
}
/**
* Save a new model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
protected function performInsert(Model $model)
{
// Create a new key if needed.
if ( ! $model->getAttribute('_id'))
{
$model->setAttribute('_id', new MongoId);
}
$result = $this->query->update(array($this->localKey => $model->getAttributes()));
if ($result) $this->associate($model);
return $result ? $model : false;
}
/**
* Save an existing model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return Model|bool
*/
protected function performUpdate(Model $model)
{
$result = $this->query->update(array($this->localKey => $model->getAttributes()));
if ($result) $this->associate($model);
return $result ? $model : false;
}
/**
* Delete all embedded models.
*
* @return int
*/
public function delete()
{
// Overwrite the local key with an empty array.
$result = $this->query->update(array($this->localKey => null));
// If the update query was successful, we will remove the embedded records
// of the parent instance.
if ($result)
{
$count = $this->count();
$this->setEmbedded(null);
// Return the number of deleted embedded records.
return $count;
}
return $result;
}
}
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Collection;
use MongoId;
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 \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $localKey
* @param string $foreignKey
* @param string $relation
* @return void
*/
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;
$this->addConstraints();
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
if (static::$constraints)
{
$this->query->where($this->parent->getKeyName(), '=', $this->parent->getKey());
}
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
// There are no eager loading constraints.
}
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param string $relation
* @return void
*/
public function initRelation(array $models, $relation)
{
foreach ($models as $model)
{
$model->setRelation($relation, $this->related->newCollection());
}
return $models;
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
foreach ($models as $model)
{
$results = $model->$relation()->getResults();
$model->setRelation($relation, $results);
}
return $models;
}
/**
* Shorthand to get the results of the relationship.
*
* @return Illuminate\Database\Eloquent\Collection
*/
public function get()
{
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 \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model)
{
if ($this->fireModelEvent($model, 'saving') === false) return false;
$this->updateTimestamps($model);
// Attach a new model.
if ( ! $this->contains($model))
{
if ($this->fireModelEvent($model, 'creating') === false) return false;
$result = $this->performInsert($model);
if ($result)
{
$this->fireModelEvent($model, 'created', false);
// Mark model as existing
$model->exists = true;
}
}
// Update an existing model.
else
{
if ($this->fireModelEvent($model, 'updating') === false) return false;
$result = $this->performUpdate($model);
if ($result) $this->fireModelEvent($model, 'updated', false);
}
if ($result)
{
$this->fireModelEvent($result, 'saved', false);
return $result;
}
return false;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\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();
$instance->setRawAttributes($attributes);
return $this->save($instance);
}
/**
* Attach an array of models to the parent instance.
*
* @param array $models
* @return array
*/
public function saveMany(array $models)
{
array_walk($models, array($this, 'save'));
return $models;
}
/**
* Create an array of new instances of the related model.
*
* @param array $records
* @return array
*/
public function createMany(array $records)
{
$instances = array_map(array($this, 'create'), $records);
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 (! is_array($ids)) $ids = array($ids);
foreach ($ids as &$id)
{
if ($id instanceof Model) $id = $id->getKey();
}
return $ids;
}
/**
* Create a related model instanced.
*
* @param array $attributes [description]
* @return [type] [description]
*/
protected function toModel($attributes = array())
{
if (is_null($attributes)) return null;
$model = $this->related->newFromBuilder((array) $attributes);
// Attatch the parent relation to the embedded model.
$model->setRelation($this->foreignKey, $this->parent);
$model->setHidden(array_merge($model->getHidden(), array($this->foreignKey)));
return $model;
}
/**
* Get the embedded records array.
*
* @return array
*/
protected function getEmbedded()
{
// Get raw attributes to skip relations and accessors.
$attributes = $this->parent->getAttributes();
return isset($attributes[$this->localKey]) ? $attributes[$this->localKey] : null;
}
/**
* Set the embedded records array.
*
* @param array $models
* @return void
*/
protected function setEmbedded($data)
{
$attributes = $this->parent->getAttributes();
$attributes[$this->localKey] = $data;
// Set raw attributes to skip mutators.
$this->parent->setRawAttributes($attributes);
// Set the relation on the parent.
$this->parent->setRelation($this->relation, $this->getResults());
}
/**
* Update the creation and update timestamps.
*
* @return void
*/
protected function updateTimestamps(Model $model)
{
// Check if this model uses timestamps first.
if ( ! $model->timestamps) return;
$time = $model->freshTimestamp();
if ( ! $model->isDirty(Model::UPDATED_AT))
{
$model->setUpdatedAt($time);
}
if ( ! $model->exists && ! $model->isDirty(Model::CREATED_AT))
{
$model->setCreatedAt($time);
}
}
/**
* 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 Illuminate\Database\Eloquent\Collection
*/
protected function toCollection(array $records = array())
{
$models = array();
// Wrap records in model objects.
foreach ($records as $attributes)
{
$models[] = $this->toModel($attributes);
}
if (count($models) > 0)
{
$models = $this->eagerLoadRelations($models);
}
return $this->related->newCollection($models);
}
/**
* Fire the given event for the given model.
*
* @param string $event
* @param bool $halt
* @return mixed
*/
protected function fireModelEvent(Model $model, $event, $halt = true)
{
$dispatcher = $model->getEventDispatcher();
if ( is_null($dispatcher)) return true;
// We will append the names of the class to the event to distinguish it from
// other model events that are fired, allowing us to listen on each model
// event set individually instead of catching event for all the models.
$event = "eloquent.{$event}: ".get_class($model);
$method = $halt ? 'until' : 'fire';
return $dispatcher->$method($event, $model);
}
}
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany as EloquentHasMany;
class HasMany extends EloquentHasMany {
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$foreignKey = $this->getHasCompareKey();
return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true);
}
}
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasOne as EloquentHasOne;
class HasOne extends EloquentHasOne {
/**
* Add the constraints for a relationship count query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Builder $parent
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getRelationCountQuery(Builder $query, Builder $parent)
{
$foreignKey = $this->getHasCompareKey();
return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true);
}
}
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Database\Eloquent\Relations\MorphTo as EloquentMorphTo;
class MorphTo extends BelongsTo {
class MorphTo extends EloquentMorphTo {
/**
* The type of the polymorphic relation.
*
* @var string
*/
protected $morphType;
/**
* The models whose relations are being eager loaded.
*
* @var \Illuminate\Database\Eloquent\Collection
*/
protected $models;
/**
* All of the models keyed by ID.
*
* @var array
*/
protected $dictionary = array();
/**
* Create a new belongs to relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $foreignKey
* @param string $otherKey
* @param string $type
* @param string $relation
* @return void
*/
public function __construct(Builder $query, Model $parent, $foreignKey, $otherKey, $type, $relation)
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
$this->morphType = $type;
parent::__construct($query, $parent, $foreignKey, $otherKey, $relation);
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->otherKey, '=', $this->parent->{$this->foreignKey});
}
}
/**
......@@ -58,125 +32,4 @@ class MorphTo extends BelongsTo {
$this->buildDictionary($this->models = Collection::make($models));
}
/**
* Buiild a dictionary with the models.
*
* @param \Illuminate\Database\Eloquent\Models $models
* @return void
*/
protected function buildDictionary(Collection $models)
{
foreach ($models as $model)
{
if ($model->{$this->morphType})
{
$this->dictionary[$model->{$this->morphType}][$model->{$this->foreignKey}][] = $model;
}
}
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $models;
}
/**
* Get the results of the relationship.
*
* Called via eager load method of Eloquent query builder.
*
* @return mixed
*/
public function getEager()
{
foreach (array_keys($this->dictionary) as $type)
{
$this->matchToMorphParents($type, $this->getResultsByType($type));
}
return $this->models;
}
/**
* Match the results for a given type to their parents.
*
* @param string $type
* @param \Illuminate\Database\Eloquent\Collection $results
* @return void
*/
protected function matchToMorphParents($type, Collection $results)
{
foreach ($results as $result)
{
if (isset($this->dictionary[$type][$result->getKey()]))
{
foreach ($this->dictionary[$type][$result->getKey()] as $model)
{
$model->setRelation($this->relation, $result);
}
}
}
}
/**
* Get all of the relation results for a type.
*
* @param string $type
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function getResultsByType($type)
{
$instance = $this->createModelByType($type);
$key = $instance->getKeyName();
return $instance->whereIn($key, $this->gatherKeysByType($type)->all())->get();
}
/**
* Gather all of the foreign keys for a given type.
*
* @param string $type
* @return array
*/
protected function gatherKeysByType($type)
{
$foreign = $this->foreignKey;
return BaseCollection::make($this->dictionary[$type])->map(function($models) use ($foreign)
{
return head($models)->{$foreign};
})->unique();
}
/**
* Create a new model instance by type.
*
* @param string $type
* @return \Illuminate\Database\Eloquent\Model
*/
public function createModelByType($type)
{
return new $type;
}
/**
* Get the dictionary used by the relationship.
*
* @return array
*/
public function getDictionary()
{
return $this->dictionary;
}
}
......@@ -6,7 +6,10 @@ class ConnectionTest extends TestCase {
{
$connection = DB::connection('mongodb');
$this->assertInstanceOf('Jenssegers\Mongodb\Connection', $connection);
}
public function testReconnect()
{
$c1 = DB::connection('mongodb');
$c2 = DB::connection('mongodb');
$this->assertEquals(spl_object_hash($c1), spl_object_hash($c2));
......@@ -20,6 +23,9 @@ class ConnectionTest extends TestCase {
{
$connection = DB::connection('mongodb');
$this->assertInstanceOf('MongoDB', $connection->getMongoDB());
$connection = DB::connection('mongodb');
$this->assertInstanceOf('MongoClient', $connection->getMongoClient());
}
public function testCollection()
......@@ -81,4 +87,33 @@ class ConnectionTest extends TestCase {
$this->assertInstanceOf('Jenssegers\Mongodb\Schema\Builder', $schema);
}
public function testDriverName()
{
$driver = DB::connection('mongodb')->getDriverName();
$this->assertEquals('mongodb', $driver);
}
public function testAuth()
{
Config::set('database.connections.mongodb.username', 'foo');
Config::set('database.connections.mongodb.password', 'bar');
$host = Config::get('database.connections.mongodb.host');
$port = Config::get('database.connections.mongodb.port', 27017);
$database = Config::get('database.connections.mongodb.database');
$this->setExpectedException('MongoConnectionException', "Failed to connect to: $host:$port: Authentication failed on database '$database' with username 'foo': auth fails");
$connection = DB::connection('mongodb');
}
public function testCustomPort()
{
$port = 27000;
Config::set('database.connections.mongodb.port', $port);
$host = Config::get('database.connections.mongodb.host');
$database = Config::get('database.connections.mongodb.database');
$this->setExpectedException('MongoConnectionException', "Failed to connect to: $host:$port: Connection refused");
$connection = DB::connection('mongodb');
}
}
<?php
class EmbeddedRelationsTest extends TestCase {
public function tearDown()
{
Mockery::close();
User::truncate();
Book::truncate();
Item::truncate();
Role::truncate();
Client::truncate();
Group::truncate();
Photo::truncate();
}
public function testEmbedsManySave()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.created: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address = $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertInstanceOf('DateTime', $address->created_at);
$this->assertInstanceOf('DateTime', $address->updated_at);
$this->assertNotNull($address->_id);
$this->assertTrue(is_string($address->_id));
$raw = $address->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
$address = $user->addresses()->save(new Address(array('city' => 'Paris')));
$user = User::find($user->_id);
$this->assertEquals(array('London', 'Paris'), $user->addresses->lists('city'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.updated: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address->city = 'New York';
$user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertEquals(2, count($user->addresses));
$this->assertEquals(2, count($user->addresses()->get()));
$this->assertEquals(2, $user->addresses->count());
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(array('London', 'New York'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array('London', 'New York'), $freshUser->addresses->lists('city'));
$address = $user->addresses->first();
$this->assertEquals('London', $address->city);
$this->assertInstanceOf('DateTime', $address->created_at);
$this->assertInstanceOf('DateTime', $address->updated_at);
$this->assertInstanceOf('User', $address->user);
$this->assertEmpty($address->relationsToArray()); // prevent infinite loop
$user = User::find($user->_id);
$user->addresses()->save(new Address(array('city' => 'Bruxelles')));
$this->assertEquals(array('London', 'New York', 'Bruxelles'), $user->addresses->lists('city'));
$address = $user->addresses[1];
$address->city = "Manhattan";
$user->addresses()->save($address);
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManyAssociate()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address = $user->addresses()->associate($address);
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertNotNull($address->_id);
$freshUser = User::find($user->_id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
$address->city = 'Londinium';
$user->addresses()->associate($address);
$this->assertEquals(array('Londinium'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
}
public function testEmbedsManySaveMany()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol'))));
$this->assertEquals(array('London', 'Bristol'), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array('London', 'Bristol'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManyDuplicate()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$user->addresses()->save($address);
$user->addresses()->save($address);
$this->assertEquals(1, $user->addresses->count());
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$user = User::find($user->id);
$this->assertEquals(1, $user->addresses->count());
$address->city = 'Paris';
$user->addresses()->save($address);
$this->assertEquals(1, $user->addresses->count());
$this->assertEquals(array('Paris'), $user->addresses->lists('city'));
$user->addresses()->create(array('_id' => $address->_id, 'city' => 'Bruxelles'));
$this->assertEquals(1, $user->addresses->count());
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
}
public function testEmbedsManyCreate()
{
$user = User::create(array());
$address = $user->addresses()->create(array('city' => 'Bruxelles'));
$this->assertInstanceOf('Address', $address);
$this->assertTrue(is_string($address->_id));
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
$raw = $address->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles'), $freshUser->addresses->lists('city'));
$user = User::create(array());
$address = $user->addresses()->create(array('_id' => '', 'city' => 'Bruxelles'));
$this->assertTrue(is_string($address->_id));
$raw = $address->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
}
public function testEmbedsManyCreateMany()
{
$user = User::create(array());
list($bruxelles, $paris) = $user->addresses()->createMany(array(array('city' => 'Bruxelles'), array('city' => 'Paris')));
$this->assertInstanceOf('Address', $bruxelles);
$this->assertEquals('Bruxelles', $bruxelles->city);
$this->assertEquals(array('Bruxelles', 'Paris'), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManyDestroy()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::mustBe($address));
$user->addresses()->destroy($address->_id);
$this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$address = $user->addresses->first();
$user->addresses()->destroy($address);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
$user->addresses()->create(array('city' => 'Paris'));
$user->addresses()->create(array('city' => 'San Francisco'));
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris', 'San Francisco'), $freshUser->addresses->lists('city'));
$ids = $user->addresses->lists('_id');
$user->addresses()->destroy($ids);
$this->assertEquals(array(), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
list($london, $bristol, $bruxelles) = $user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$user->addresses()->destroy(array($london, $bruxelles));
$this->assertEquals(array('Bristol'), $user->addresses->lists('city'));
}
public function testEmbedsManyDissociate()
{
$user = User::create(array());
$cordoba = $user->addresses()->create(array('city' => 'Cordoba'));
$user->addresses()->dissociate($cordoba->id);
$freshUser = User::find($user->id);
$this->assertEquals(0, $user->addresses->count());
$this->assertEquals(1, $freshUser->addresses->count());
}
public function testEmbedsManyAliases()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address = $user->addresses()->attach($address);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$user->addresses()->detach($address);
$this->assertEquals(array(), $user->addresses->lists('city'));
}
public function testEmbedsManyCreatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(false);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManySavingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'Paris'));
$address->exists = true;
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(false);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManyUpdatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'New York'));
$user->addresses()->save($address);
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(false);
$address->city = 'Warsaw';
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManyDeletingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York')));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(false);
$this->assertEquals(0, $user->addresses()->destroy($address));
$this->assertEquals(array('New York'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
}
public function testEmbedsManyFindOrContains()
{
$user = User::create(array('name' => 'John Doe'));
$address1 = $user->addresses()->save(new Address(array('city' => 'New York')));
$address2 = $user->addresses()->save(new Address(array('city' => 'Paris')));
$address = $user->addresses()->find($address1->_id);
$this->assertEquals($address->city, $address1->city);
$address = $user->addresses()->find($address2->_id);
$this->assertEquals($address->city, $address2->city);
$this->assertTrue($user->addresses()->contains($address2->_id));
$this->assertFalse($user->addresses()->contains('123'));
}
public function testEmbedsManyEagerLoading()
{
$user1 = User::create(array('name' => 'John Doe'));
$user1->addresses()->save(new Address(array('city' => 'New York')));
$user1->addresses()->save(new Address(array('city' => 'Paris')));
$user2 = User::create(array('name' => 'Jane Doe'));
$user2->addresses()->save(new Address(array('city' => 'Berlin')));
$user2->addresses()->save(new Address(array('city' => 'Paris')));
$user = User::find($user1->id);
$relations = $user->getRelations();
$this->assertFalse(array_key_exists('addresses', $relations));
$user = User::with('addresses')->get()->first();
$relations = $user->getRelations();
$this->assertTrue(array_key_exists('addresses', $relations));
$this->assertEquals(2, $relations['addresses']->count());
}
public function testEmbedsManyDelete()
{
$user1 = User::create(array('name' => 'John Doe'));
$user1->addresses()->save(new Address(array('city' => 'New York')));
$user1->addresses()->save(new Address(array('city' => 'Paris')));
$user2 = User::create(array('name' => 'Jane Doe'));
$user2->addresses()->save(new Address(array('city' => 'Berlin')));
$user2->addresses()->save(new Address(array('city' => 'Paris')));
$user1->addresses()->delete();
$this->assertEquals(0, $user1->addresses()->count());
$this->assertEquals(0, $user1->addresses->count());
$this->assertEquals(2, $user2->addresses()->count());
$this->assertEquals(2, $user2->addresses->count());
$user1 = User::find($user1->id);
$user2 = User::find($user2->id);
$this->assertEquals(0, $user1->addresses()->count());
$this->assertEquals(0, $user1->addresses->count());
$this->assertEquals(2, $user2->addresses()->count());
$this->assertEquals(2, $user2->addresses->count());
}
public function testEmbedsManyCollectionMethods()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York')));
$user->addresses()->save(new Address(array('city' => 'Paris')));
$user->addresses()->save(new Address(array('city' => 'Brussels')));
$this->assertEquals(array('New York', 'Paris', 'Brussels'), $user->addresses()->lists('city'));
$this->assertEquals(array('Brussels', 'New York', 'Paris'), $user->addresses()->sortBy('city')->lists('city'));
$this->assertEquals(array('Brussels', 'New York', 'Paris'), $user->addresses()->orderBy('city')->lists('city'));
$this->assertEquals(array('Paris', 'New York', 'Brussels'), $user->addresses()->orderBy('city', 'desc')->lists('city'));
}
public function testEmbedsOne()
{
$user = User::create(array('name' => 'John Doe'));
$father = new User(array('name' => 'Mark Doe'));
$father->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($father), $father)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($father), $father)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.created: '.get_class($father), $father);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($father), $father);
$father = $user->father()->save($father);
$father->unsetEventDispatcher();
$this->assertNotNull($user->_father);
$this->assertEquals('Mark Doe', $user->father->name);
$this->assertInstanceOf('DateTime', $father->created_at);
$this->assertInstanceOf('DateTime', $father->updated_at);
$this->assertNotNull($father->_id);
$this->assertTrue(is_string($father->_id));
$raw = $father->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
$father->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($father), $father)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($father), $father)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.updated: '.get_class($father), $father);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($father), $father);
$father->name = 'Tom Doe';
$user->father()->save($father);
$father->unsetEventDispatcher();
$this->assertNotNull($user->_father);
$this->assertEquals('Tom Doe', $user->father->name);
$father = new User(array('name' => 'Jim Doe'));
$father->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($father), $father)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($father), $father)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.created: '.get_class($father), $father);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($father), $father);
$father = $user->father()->save($father);
$father->unsetEventDispatcher();
$this->assertNotNull($user->_father);
$this->assertEquals('Jim Doe', $user->father->name);
}
public function testEmbedsOneAssociate()
{
$user = User::create(array('name' => 'John Doe'));
$father = new User(array('name' => 'Mark Doe'));
$father->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->times(0)->with('eloquent.saving: '.get_class($father), $father);
$father = $user->father()->associate($father);
$father->unsetEventDispatcher();
$this->assertNotNull($user->_father);
$this->assertEquals('Mark Doe', $user->father->name);
}
public function testEmbedsOneDelete()
{
$user = User::create(array('name' => 'John Doe'));
$father = $user->father()->save(new User(array('name' => 'Mark Doe')));
$user->father()->delete();
$this->assertNull($user->_father);
$this->assertNull($user->father);
}
}
......@@ -34,10 +34,14 @@ class ModelTest extends TestCase {
$this->assertEquals(1, User::count());
$this->assertTrue(isset($user->_id));
$this->assertTrue(is_string($user->_id));
$this->assertNotEquals('', (string) $user->_id);
$this->assertNotEquals(0, strlen((string) $user->_id));
$this->assertInstanceOf('Carbon\Carbon', $user->created_at);
$raw = $user->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
$this->assertEquals('John Doe', $user->name);
$this->assertEquals(35, $user->age);
}
......@@ -50,6 +54,9 @@ class ModelTest extends TestCase {
$user->age = 35;
$user->save();
$raw = $user->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
$check = User::find($user->_id);
$check->age = 36;
......@@ -65,6 +72,9 @@ class ModelTest extends TestCase {
$user->update(array('age' => 20));
$raw = $user->getAttributes();
$this->assertInstanceOf('MongoId', $raw['_id']);
$check = User::find($user->_id);
$this->assertEquals(20, $check->age);
}
......@@ -277,6 +287,7 @@ class ModelTest extends TestCase {
$this->assertEquals(array('_id', 'created_at', 'name', 'type', 'updated_at'), $keys);
$this->assertTrue(is_string($array['created_at']));
$this->assertTrue(is_string($array['updated_at']));
$this->assertTrue(is_string($array['_id']));
}
public function testUnset()
......@@ -308,7 +319,8 @@ class ModelTest extends TestCase {
public function testDates()
{
$user = User::create(array('name' => 'John Doe', 'birthday' => new DateTime('1980/1/1')));
$birthday = new DateTime('1980/1/1');
$user = User::create(array('name' => 'John Doe', 'birthday' => $birthday));
$this->assertInstanceOf('Carbon\Carbon', $user->birthday);
$check = User::find($user->_id);
......@@ -317,6 +329,25 @@ class ModelTest extends TestCase {
$user = User::where('birthday', '>', new DateTime('1975/1/1'))->first();
$this->assertEquals('John Doe', $user->name);
// test custom date format for json output
$json = $user->toArray();
$this->assertEquals($user->birthday->format('l jS \of F Y h:i:s A'), $json['birthday']);
$this->assertEquals($user->created_at->format('l jS \of F Y h:i:s A'), $json['created_at']);
// test default date format for json output
$item = Item::create(array('name' => 'sword'));
$json = $item->toArray();
$this->assertEquals($item->created_at->format('Y-m-d H:i:s'), $json['created_at']);
$user = User::create(array('name' => 'Jane Doe', 'birthday' => time()));
$this->assertInstanceOf('Carbon\Carbon', $user->birthday);
$user = User::create(array('name' => 'Jane Doe', 'birthday' => 'Monday 8th of August 2005 03:12:46 PM'));
$this->assertInstanceOf('Carbon\Carbon', $user->birthday);
$user = User::create(array('name' => 'Jane Doe', 'birthday' => '2005-08-08'));
$this->assertInstanceOf('Carbon\Carbon', $user->birthday);
}
public function testIdAttribute()
......
......@@ -45,7 +45,7 @@ class MysqlRelationsTest extends TestCase {
$user = MysqlUser::find($user->id); // refetch
$this->assertEquals('admin', $user->role->type);
// MongoDB beelongs to
// MongoDB belongs to
$role = $user->role()->first(); // refetch
$this->assertEquals('John Doe', $role->mysqlUser->name);
......
......@@ -54,9 +54,7 @@ class QueryBuilderTest extends TestCase {
public function testInsertGetId()
{
$id = DB::collection('users')->insertGetId(array('name' => 'John Doe'));
$this->assertTrue(is_string($id));
$this->assertEquals(24, strlen($id));
$this->assertInstanceOf('MongoId', $id);
}
public function testBatchInsert()
......@@ -534,6 +532,27 @@ class QueryBuilderTest extends TestCase {
$results = DB::collection('users')->where('name', 'REGEX', $regex)->get();
$this->assertEquals(2, count($results));
DB::collection('users')->insert(array(
array(
'name' => 'John Doe',
'addresses' => array(
array('city' => 'Ghent'),
array('city' => 'Paris')
)
),
array(
'name' => 'Jane Doe',
'addresses' => array(
array('city' => 'Brussels'),
array('city' => 'Paris')
)
)
));
$users = DB::collection('users')->where('addresses', 'elemMatch', array('city' => 'Brussels'))->get();
$this->assertEquals(1, count($users));
$this->assertEquals('Jane Doe', $users[0]['name']);
}
public function testIncrement()
......
......@@ -255,4 +255,29 @@ class QueryTest extends TestCase {
$this->assertEquals(6, count($users));
}
public function testMultipleOr()
{
$users = User::where(function($query)
{
$query->where('age', 35)->orWhere('age', 33);
})
->where(function($query)
{
$query->where('name', 'John Doe')->orWhere('name', 'Jane Doe');
})->get();
$this->assertEquals(2, count($users));
$users = User::where(function($query)
{
$query->orWhere('age', 35)->orWhere('age', 33);
})
->where(function($query)
{
$query->orWhere('name', 'John Doe')->orWhere('name', 'Jane Doe');
})->get();
$this->assertEquals(2, count($users));
}
}
......@@ -57,6 +57,20 @@ class RelationsTest extends TestCase {
$role = $user->role;
$this->assertEquals('admin', $role->type);
$this->assertEquals($user->_id, $role->user_id);
$user = User::create(array('name' => 'Jane Doe'));
$role = new Role(array('type' => 'user'));
$user->role()->save($role);
$role = $user->role;
$this->assertEquals('user', $role->type);
$this->assertEquals($user->_id, $role->user_id);
$user = User::where('name', 'Jane Doe')->first();
$role = $user->role;
$this->assertEquals('user', $role->type);
$this->assertEquals($user->_id, $role->user_id);
}
public function testWithBelongsTo()
......@@ -287,247 +301,95 @@ class RelationsTest extends TestCase {
$photo = Photo::first();
$this->assertEquals($photo->imageable->name, $user->name);
}
public function testEmbedsManySave()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.created: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address = $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertInstanceOf('DateTime', $address->created_at);
$this->assertInstanceOf('DateTime', $address->updated_at);
$this->assertNotNull($address->_id);
$address = $user->addresses()->save(new Address(array('city' => 'Paris')));
$user = User::find($user->_id);
$this->assertEquals(array('London', 'Paris'), $user->addresses->lists('city'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.updated: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address->city = 'New York';
$user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertEquals(2, count($user->addresses));
$this->assertEquals(2, count($user->addresses()->get()));
$this->assertEquals(2, $user->addresses->count());
$this->assertEquals(array('London', 'New York'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array('London', 'New York'), $freshUser->addresses->lists('city'));
$address = $user->addresses->first();
$this->assertEquals('London', $address->city);
$this->assertInstanceOf('DateTime', $address->created_at);
$this->assertInstanceOf('DateTime', $address->updated_at);
$this->assertInstanceOf('User', $address->user);
$this->assertEmpty($address->relationsToArray()); // prevent infinite loop
$user = User::find($user->_id);
$user->addresses()->save(new Address(array('city' => 'Bruxelles')));
$this->assertEquals(array('London', 'New York', 'Bruxelles'), $user->addresses->lists('city'));
$address = $user->addresses[1];
$address->city = "Manhattan";
$user->addresses()->save($address);
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManyAssociate()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address = $user->addresses()->associate($address);
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertNotNull($address->_id);
$freshUser = User::find($user->_id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
$address->city = 'Londinium';
$user->addresses()->associate($address);
$this->assertEquals(array('Londinium'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
}
public function testEmbedsManySaveMany()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol'))));
$this->assertEquals(array('London', 'Bristol'), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array('London', 'Bristol'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManyCreate()
{
$user = User::create(array());
$address = $user->addresses()->create(array('city' => 'Bruxelles'));
$this->assertInstanceOf('Address', $address);
$this->assertInstanceOf('MongoID', $address->_id);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles'), $freshUser->addresses->lists('city'));
$user = User::create(array());
$address = $user->addresses()->create(array('_id' => '', 'city' => 'Bruxelles'));
$this->assertInstanceOf('MongoID', $address->_id);
}
public function testEmbedsManyCreateMany()
{
$user = User::create(array());
list($bruxelles, $paris) = $user->addresses()->createMany(array(array('city' => 'Bruxelles'), array('city' => 'Paris')));
$this->assertInstanceOf('Address', $bruxelles);
$this->assertEquals('Bruxelles', $bruxelles->city);
$this->assertEquals(array('Bruxelles', 'Paris'), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManyDestroy()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::mustBe($address));
$user->addresses()->destroy($address->_id);
$this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$address = $user->addresses->first();
$user->addresses()->destroy($address);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
$user = User::with('photos')->find($user->_id);
$relations = $user->getRelations();
$this->assertTrue(array_key_exists('photos', $relations));
$this->assertEquals(1, $relations['photos']->count());
$user->addresses()->create(array('city' => 'Paris'));
$user->addresses()->create(array('city' => 'San Francisco'));
$photos = Photo::with('imageable')->get();
$relations = $photos[0]->getRelations();
$this->assertTrue(array_key_exists('imageable', $relations));
$this->assertInstanceOf('User', $relations['imageable']);
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris', 'San Francisco'), $freshUser->addresses->lists('city'));
$ids = $user->addresses->lists('_id');
$user->addresses()->destroy($ids);
$this->assertEquals(array(), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
list($london, $bristol, $bruxelles) = $user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$user->addresses()->destroy(array($london, $bruxelles));
$this->assertEquals(array('Bristol'), $user->addresses->lists('city'));
$relations = $photos[1]->getRelations();
$this->assertTrue(array_key_exists('imageable', $relations));
$this->assertInstanceOf('Client', $relations['imageable']);
}
public function testEmbedsManyDissociate()
public function testHasManyHas()
{
$user = User::create(array());
$cordoba = $user->addresses()->create(array('city' => 'Cordoba'));
$user->addresses()->dissociate($cordoba->id);
$author1 = User::create(array('name' => 'George R. R. Martin'));
$author1->books()->create(array('title' => 'A Game of Thrones', 'rating' => 5));
$author1->books()->create(array('title' => 'A Clash of Kings', 'rating' => 5));
$author2 = User::create(array('name' => 'John Doe'));
$author2->books()->create(array('title' => 'My book', 'rating' => 2));
User::create(array('name' => 'Anonymous author'));
Book::create(array('title' => 'Anonymous book', 'rating' => 1));
$freshUser = User::find($user->id);
$this->assertEquals(0, $user->addresses->count());
$this->assertEquals(1, $freshUser->addresses->count());
}
$authors = User::has('books')->get();
$this->assertCount(2, $authors);
$this->assertEquals('George R. R. Martin', $authors[0]->name);
$this->assertEquals('John Doe', $authors[1]->name);
public function testEmbedsManyAliases()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$authors = User::has('books', '>', 1)->get();
$this->assertCount(1, $authors);
$address = $user->addresses()->attach($address);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$authors = User::has('books', '<', 5)->get();
$this->assertCount(3, $authors);
$user->addresses()->detach($address);
$this->assertEquals(array(), $user->addresses->lists('city'));
}
$authors = User::has('books', '>=', 2)->get();
$this->assertCount(1, $authors);
public function testEmbedsManyCreatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(false);
$authors = User::has('books', '<=', 1)->get();
$this->assertCount(2, $authors);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
$authors = User::has('books', '=', 2)->get();
$this->assertCount(1, $authors);
public function testEmbedsManySavingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'Paris'));
$address->exists = true;
$authors = User::has('books', '!=', 2)->get();
$this->assertCount(2, $authors);
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(false);
$authors = User::has('books', '=', 0)->get();
$this->assertCount(1, $authors);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
$authors = User::has('books', '!=', 0)->get();
$this->assertCount(2, $authors);
public function testEmbedsManyUpdatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'New York'));
$address->exists = true;
$authors = User::whereHas('books', function($query)
{
$query->where('rating', 5);
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(false);
})->get();
$this->assertCount(1, $authors);
$address->city = 'Warsaw';
$authors = User::whereHas('books', function($query)
{
$query->where('rating', '<', 5);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
})->get();
$this->assertCount(1, $authors);
}
public function testEmbedsManyDeletingEventReturnsFalse()
public function testHasOneHas()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York')));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(false);
$this->assertEquals(0, $user->addresses()->destroy($address));
$this->assertEquals(array('New York'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$user1 = User::create(array('name' => 'John Doe'));
$user1->role()->create(array('title' => 'admin'));
$user2 = User::create(array('name' => 'Jane Doe'));
$user2->role()->create(array('title' => 'reseller'));
User::create(array('name' => 'Mark Moe'));
Role::create(array('title' => 'Customer'));
$users = User::has('role')->get();
$this->assertCount(2, $users);
$this->assertEquals('John Doe', $users[0]->name);
$this->assertEquals('Jane Doe', $users[1]->name);
$users = User::has('role', '=', 0)->get();
$this->assertCount(1, $users);
$users = User::has('role', '!=', 0)->get();
$this->assertCount(2, $users);
}
}
......@@ -114,11 +114,11 @@ class SchemaTest extends TestCase {
{
Schema::collection('newcollection', function($collection)
{
$collection->background('backgroundkey');
$collection->sparse('sparsekey');
});
$index = $this->getIndex('newcollection', 'backgroundkey');
$this->assertEquals(1, $index['background']);
$index = $this->getIndex('newcollection', 'sparsekey');
$this->assertEquals(1, $index['sparse']);
}
public function testExpire()
......
......@@ -32,6 +32,9 @@ class TestCase extends Orchestra\Testbench\TestCase {
// overwrite database configuration
$app['config']->set('database.connections.mysql', $config['connections']['mysql']);
$app['config']->set('database.connections.mongodb', $config['connections']['mongodb']);
// overwrite cache configuration
$app['config']->set('cache.driver', 'array');
}
}
......@@ -16,7 +16,7 @@ class MysqlRole extends Eloquent {
public function mysqlUser()
{
return $this->belongsTo('MysqlUser', 'role_id');
return $this->belongsTo('MysqlUser');
}
/**
......
......@@ -16,7 +16,7 @@ class MysqlUser extends Eloquent {
public function role()
{
return $this->hasOne('Role', 'role_id');
return $this->hasOne('Role');
}
/**
......
......@@ -14,7 +14,7 @@ class Role extends Eloquent {
public function mysqlUser()
{
return $this->belongsTo('MysqlUser', 'role_id');
return $this->belongsTo('MysqlUser');
}
}
......@@ -7,7 +7,6 @@ use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $collection = 'users';
protected $dates = array('birthday');
protected static $unguarded = true;
......@@ -56,6 +55,11 @@ class User extends Eloquent implements UserInterface, RemindableInterface {
return $this->embedsMany('Address');
}
public function father()
{
return $this->embedsOne('User');
}
/**
* Get the unique identifier for the user.
*
......@@ -85,4 +89,39 @@ class User extends Eloquent implements UserInterface, RemindableInterface {
{
return $this->email;
}
/**
* Get the token value for the "remember me" session.
*
* @return string
*/
public function getRememberToken()
{
return $this->rememberToken;
}
/**
* Set the token value for the "remember me" session.
*
* @param string $value
* @return void
*/
public function setRememberToken($value)
{
$this->rememberToken = $value;
}
/**
* Get the column name for the "remember me" token.
*
* @return string
*/
public function getRememberTokenName() {
return 'remember_token';
}
protected function getDateFormat()
{
return 'l jS \of F Y h:i:s A';
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment