Commit 0ec7a248 authored by Jens Segers's avatar Jens Segers

Merge branch 'master' into develop

parents f7210153 040b7944
...@@ -5,3 +5,4 @@ composer.phar ...@@ -5,3 +5,4 @@ composer.phar
composer.lock composer.lock
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
*.project
...@@ -65,10 +65,33 @@ Tell your model to use the MongoDB model and set the collection (alias for table ...@@ -65,10 +65,33 @@ Tell your model to use the MongoDB model and set the collection (alias for table
} }
*You can also specify the connection name in the model by changing the `connection` property.* If you are using a different database driver as the default one, you will need to specify the mongodb connection within your model by changing the `connection` property:
use Jenssegers\Mongodb\Model as Eloquent;
class MyModel extends Eloquent {
protected $connection = 'mongodb';
}
Everything else works just like the original Eloquent model. Read more about the Eloquent on http://laravel.com/docs/eloquent Everything else works just like the original Eloquent model. Read more about the Eloquent on http://laravel.com/docs/eloquent
### Optional: Alias
-------------------
You may also register an alias for the MongoDB model by adding the following to the alias array in `app/config/app.php`:
'Moloquent' => 'Jenssegers\Mongodb\Model',
This will allow you to use your registered alias like:
class MyModel extends Moloquent {
protected $collection = 'mycollection';
}
Query Builder Query Builder
------------- -------------
...@@ -135,7 +158,7 @@ Examples ...@@ -135,7 +158,7 @@ Examples
$users = User::whereIn('age', array(16, 18, 20))->get(); $users = User::whereIn('age', array(16, 18, 20))->get();
When using `whereNotIn` objects will be returned if the field is non existant. Combine with `whereNotNull('age')` to leave out those documents. When using `whereNotIn` objects will be returned if the field is non existent. Combine with `whereNotNull('age')` to leave out those documents.
**Using Where Between** **Using Where Between**
...@@ -243,6 +266,9 @@ Supported relations are: ...@@ -243,6 +266,9 @@ Supported relations are:
- hasOne - hasOne
- hasMany - hasMany
- belongsTo - belongsTo
- belongsToMany
*The belongsToMany relation will not use a pivot "table", but will push id's to a **related_ids** attribute instead.*
Example: Example:
......
...@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; ...@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Jenssegers\Mongodb\DatabaseManager as Resolver; use Jenssegers\Mongodb\DatabaseManager as Resolver;
use Jenssegers\Mongodb\Builder as QueryBuilder; use Jenssegers\Mongodb\Builder as QueryBuilder;
use Jenssegers\Mongodb\Relations\BelongsTo; use Jenssegers\Mongodb\Relations\BelongsTo;
use Jenssegers\Mongodb\Relations\BelongsToMany;
use Carbon\Carbon; use Carbon\Carbon;
use DateTime; use DateTime;
...@@ -199,6 +200,43 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model { ...@@ -199,6 +200,43 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model {
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation); return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
} }
/**
* Define a many-to-many relationship.
*
* @param string $related
* @param string $table
* @param string $foreignKey
* @param string $otherKey
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function belongsToMany($related, $collection = null, $foreignKey = null, $otherKey = null)
{
$caller = $this->getBelongsToManyCaller();
// First, we'll need to determine the foreign key and "other key" for the
// relationship. Once we have determined the keys we'll make the query
// instances as well as the relationship instances we need for this.
$foreignKey = $foreignKey ?: $this->getForeignKey() . 's';
$instance = new $related;
$otherKey = $otherKey ?: $instance->getForeignKey() . 's';
// If no table name was provided, we can guess it by concatenating the two
// models using underscores in alphabetical order. The two model names
// are transformed to snake case from their default CamelCase also.
if (is_null($collection))
{
$collection = snake_case(str_plural(class_basename($related)));
}
// Now we're ready to create a new query builder for the related model and
// the relationship instances for the relation. The relations will set
// appropriate query constraint and entirely manages the hydrations.
$query = $instance->newQuery();
return new BelongsToMany($query, $this, $collection, $foreignKey, $otherKey, $caller['function']);
}
/** /**
* Get a new query builder instance for the connection. * Get a new query builder instance for the connection.
* *
......
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Collection;
use Jenssegers\Mongodb\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMany;
class BelongsToMany extends EloquentBelongsToMany {
/**
* Hydrate the pivot table relationship on the models.
*
* @param array $models
* @return void
*/
protected function hydratePivotRelation(array $models)
{
// Do nothing
}
/**
* Set the select clause for the relation query.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
protected function getSelectColumns(array $columns = array('*'))
{
return $columns;
}
/**
* Set the base constraints on the relation query.
*
* @return void
*/
public function addConstraints()
{
if (static::$constraints)
{
// Make sure that the primary key of the parent
// is in the relationship array of keys
$this->query->whereIn($this->foreignKey, array($this->parent->getKey()));
}
}
/**
* Sync the intermediate tables with a list of IDs.
*
* @param array $ids
* @param bool $detaching
* @return void
*/
public function sync(array $ids, $detaching = true)
{
// 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();
$records = $this->formatSyncList($ids);
$detach = array_diff($current, array_keys($records));
// Next, we will take the differences of the currents and given IDs and detach
// all of the entities that exist in the "current" array but are not in the
// the array of the IDs given to the method which will complete the sync.
if ($detaching and count($detach) > 0)
{
$this->detach($detach);
}
// Now we are finally ready to attach the new records. Note that we'll disable
// touching until after the entire operation is complete so we don't fire a
// ton of touch operations until we are totally done syncing the records.
$this->attachNew($records, $current, false);
$this->touchIfTouching();
}
/**
* Attach all of the IDs that aren't in the current array.
*
* @param array $records
* @param array $current
* @param bool $touch
* @return void
*/
protected function attachNew(array $records, array $current, $touch = true)
{
foreach ($records as $id => $attributes)
{
// If the ID is not in the list of existing pivot IDs, we will insert a new pivot
// record, otherwise, we will just update this existing record on this joining
// table, so that the developers will easily update these records pain free.
if ( ! in_array($id, $current))
{
$this->attach($id, $attributes, $touch);
}
}
}
/**
* Attach a model to the parent.
*
* @param mixed $id
* @param array $attributes
* @param bool $touch
* @return void
*/
public function attach($id, array $attributes = array(), $touch = true)
{
if ($id instanceof Model) $id = $id->getKey();
// Generate a new parent query instance
$parent = $this->newParentQuery();
// Generate a new related query instance
$related = $this->related->newInstance();
// Set contraints on the related query
$related = $related->where($this->related->getKeyName(), $id);
$records = $this->createAttachRecords((array) $id, $attributes);
// Get the ID's to attach to the two documents
$otherIds = array_pluck($records, $this->otherKey);
$foreignIds = array_pluck($records, $this->foreignKey);
// Attach to the parent model
$parent->push($this->otherKey, $otherIds[0])->update(array());
// Attach to the related model
$related->push($this->foreignKey, $foreignIds[0])->update(array());
}
/**
* 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.
*
* @param int|array $ids
* @param bool $touch
* @return int
*/
public function detach($ids = array(), $touch = true)
{
if ($ids instanceof Model) $ids = (array) $ids->getKey();
$query = $this->newParentQuery();
// If associated IDs were passed to the method we will only delete those
// associations, otherwise all of the association ties will be broken.
// We'll return the numbers of affected rows when we do the deletes.
$ids = (array) $ids;
if (count($ids) > 0)
{
$query->whereIn($this->otherKey, $ids);
}
if ($touch) $this->touchIfTouching();
// Once we have all of the conditions set on the statement, we are ready
// to run the delete on the pivot table. Then, if the touch parameter
// is true, we will go ahead and touch all related models to sync.
foreach($ids as $id)
{
$query->pull($this->otherKey, $id);
}
return count($ids);
}
/**
* Create a new query builder for the parent
*
* @return Jenssegers\Mongodb\Builder
*/
protected function newParentQuery()
{
$query = $this->parent->newQuery();
return $query->where($this->parent->getKeyName(), '=', $this->parent->getKey());
}
/**
* Build model dictionary keyed by the relation's foreign key.
*
* @param \Illuminate\Database\Eloquent\Collection $results
* @return array
*/
protected function buildDictionary(Collection $results)
{
$foreign = $this->foreignKey;
// First we will build a dictionary of child models keyed by the foreign key
// of the relation so that we will easily and quickly match them to their
// parents without having a possibly slow inner loops for every models.
$dictionary = array();
foreach ($results as $result)
{
foreach ($result->$foreign as $single)
{
$dictionary[$single][] = $result;
}
}
return $dictionary;
}
/**
* Get the fully qualified foreign key for the relation.
*
* @return string
*/
public function getForeignKey()
{
return $this->foreignKey;
}
/**
* Get the fully qualified "other key" for the relation.
*
* @return string
*/
public function getOtherKey()
{
return $this->otherKey;
}
}
...@@ -312,12 +312,15 @@ class ModelTest extends PHPUnit_Framework_TestCase { ...@@ -312,12 +312,15 @@ class ModelTest extends PHPUnit_Framework_TestCase {
public function testDates() public function testDates()
{ {
$user = User::create(array('name' => 'John Doe', 'birthday' => new DateTime('1980/1/1'))); $user = User::create(array('name' => 'John Doe', 'birthday' => new DateTime('1980/1/1')));
$this->assertInstanceOf('Carbon\Carbon', $user->birthday); $this->assertInstanceOf('Carbon\Carbon', $user->birthday);
$check = User::find($user->_id); $check = User::find($user->_id);
$this->assertInstanceOf('Carbon\Carbon', $check->birthday); $this->assertInstanceOf('Carbon\Carbon', $check->birthday);
$this->assertEquals($user->birthday, $check->birthday); $this->assertEquals($user->birthday, $check->birthday);
$user = User::where('birthday', '>', new DateTime('1975/1/1'))->first(); $user = User::where('birthday', '>', new DateTime('1975/1/1'))->first();
$this->assertEquals('John Doe', $user->name); $this->assertEquals('John Doe', $user->name);
} }
......
...@@ -11,6 +11,7 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -11,6 +11,7 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
Book::truncate(); Book::truncate();
Item::truncate(); Item::truncate();
Role::truncate(); Role::truncate();
Client::truncate();
} }
public function testHasMany() public function testHasMany()
...@@ -30,7 +31,7 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -30,7 +31,7 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$items = $user->items; $items = $user->items;
$this->assertEquals(3, count($items)); $this->assertEquals(3, count($items));
} }
public function testBelongsTo() public function testBelongsTo()
{ {
...@@ -102,4 +103,113 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -102,4 +103,113 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('admin', $role->type); $this->assertEquals('admin', $role->type);
} }
public function testEasyRelation()
{
// Has Many
$user = User::create(array('name' => 'John Doe'));
$item = Item::create(array('type' => 'knife'));
$user->items()->save($item);
$user = User::find($user->_id);
$items = $user->items;
$this->assertEquals(1, count($items));
$this->assertInstanceOf('Item', $items[0]);
// Has one
$user = User::create(array('name' => 'John Doe'));
$role = Role::create(array('type' => 'admin'));
$user->role()->save($role);
$user = User::find($user->_id);
$role = $user->role;
$this->assertInstanceOf('Role', $role);
$this->assertEquals('admin', $role->type);
}
public function testHasManyAndBelongsTo()
{
$user = User::create(array('name' => 'John Doe'));
$user->clients()->save(new Client(array('name' => 'Pork Pies Ltd.')));
$user->clients()->create(array('name' => 'Buffet Bar Inc.'));
$user = User::with('clients')->find($user->_id);
$client = Client::with('users')->first();
$clients = $client->getRelation('users');
$users = $user->getRelation('clients');
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $users);
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $clients);
$this->assertInstanceOf('Client', $users[0]);
$this->assertInstanceOf('User', $clients[0]);
$this->assertCount(2, $user->clients);
$this->assertCount(1, $client->users);
// Now create a new user to an existing client
$client->users()->create(array('name' => 'Jane Doe'));
$otherClient = User::where('name', '=', 'Jane Doe')->first()->clients()->get();
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $otherClient);
$this->assertInstanceOf('Client', $otherClient[0]);
$this->assertCount(1, $otherClient);
// Now attach an existing client to an existing user
$user = User::where('name', '=', 'Jane Doe')->first();
$client = Client::Where('name', '=', 'Buffet Bar Inc.')->first();
// Check the models are what they should be
$this->assertInstanceOf('Client', $client);
$this->assertInstanceOf('User', $user);
// Assert they are not attached
$this->assertFalse(in_array($client->_id, $user->client_ids));
$this->assertFalse(in_array($user->_id, $client->user_ids));
// Attach the client to the user
$user->clients()->attach($client);
// Get the new user model
$user = User::where('name', '=', 'Jane Doe')->first();
$client = Client::Where('name', '=', 'Buffet Bar Inc.')->first();
// Assert they are attached
$this->assertTrue(in_array($client->_id, $user->client_ids));
$this->assertTrue(in_array($user->_id, $client->user_ids));
}
public function testHasManyAndBelongsToAttachesExistingModels()
{
$user = User::create(array('name' => 'John Doe', 'client_ids' => array('1234523')));
$clients = array(
Client::create(array('name' => 'Pork Pies Ltd.'))->_id,
Client::create(array('name' => 'Buffet Bar Inc.'))->_id
);
$moreClients = array(
Client::create(array('name' => 'Boloni Ltd.'))->_id,
Client::create(array('name' => 'Meatballs Inc.'))->_id
);
// Sync multiple records
$user->clients()->sync($clients);
$user = User::with('clients')->find($user->_id);
// Assert non attached ID's are detached succesfully
$this->assertFalse(in_array('1234523', $user->client_ids));
// Assert there are two client objects in the relationship
$this->assertCount(2, $user->clients);
$user->clients()->sync($moreClients);
$user = User::with('clients')->find($user->_id);
// Assert there are now 4 client objects in the relationship
$this->assertCount(4, $user->clients);
}
} }
<?php
use Jenssegers\Mongodb\Model as Eloquent;
class Client extends Eloquent {
protected $collection = 'clients';
protected static $unguarded = true;
public function users()
{
return $this->belongsToMany('User');
}
}
\ No newline at end of file
...@@ -28,6 +28,11 @@ class User extends Eloquent implements UserInterface, RemindableInterface { ...@@ -28,6 +28,11 @@ class User extends Eloquent implements UserInterface, RemindableInterface {
return $this->hasOne('Role'); return $this->hasOne('Role');
} }
public function clients()
{
return $this->belongsToMany('Client');
}
/** /**
* Get the unique identifier for the user. * Get the unique identifier for the user.
* *
...@@ -57,5 +62,4 @@ class User extends Eloquent implements UserInterface, RemindableInterface { ...@@ -57,5 +62,4 @@ class User extends Eloquent implements UserInterface, RemindableInterface {
{ {
return $this->email; return $this->email;
} }
} }
\ No newline at end of file
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