Giter VIP home page Giter VIP logo

shanty-mongo's Introduction

RETIRED

This Library has be retired until further notice. There are a number of critical bugs that this library contains. They can be fixed but I don't have the time at the moment and I feel it is irresponsible to let people continue to use this library knowing the issues they may experience. I welcome pull requests! With tests of course!

Shanty Mongo

Build Status

Summary

Shanty Mongo is MongoDB library for the Zend Framework. It's intention is to make working with MongoDB documents as natural and as simple as possible. In particular allowing embedded documents to also have custom document classes.

Requirements

  • PHP 5.3 or greater
  • Zend Framework 1.10.0 or greater
  • Mongodb 1.3 or greater
  • Mongo extension from pecl

Features

  • ORM
  • Simple and flexible
  • Partial updates. Only changed data is sent back to the server. Also you can save or delete embeded documents individually.
  • Support for references (lazy loaded)
  • Support for inheritance
  • Optional schema enforcement: Validation and Filtering on properties
  • Embeded documents/documentsets can have custom document classes like the root document

How to Use

Initialisation

Use Zend's autoloader and add the library folder to your include path

Connections

If you are connecting to localhost without any authentication then no need to worry about connections any further. Shanty Mongo will connect automatically on the first request if no connections have previously been added.

Advanced connections

For information on how to configure master/slave setups, weighted connections and multiple connection goups see the wiki

Define a document/collection

To define a document and the collection that the document will be saved to, extend Shanty_Mongo_Document and set the static properties $_db and $_collection.

class User extends Shanty_Mongo_Document 
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
}

Create a new document

$user = new User();
$user->name = 'Bob';
$user->save();

// Or you can pass an array of data to the constructor as so.
// Please be aware passing data into the constructor by passes filtering and validation
// It assumes you are passing in a raw 'json' document from mongo
$data = array(
    'name' => 'Bob'
);

$user = new User($data);
$user->save();

Find a document

$user = User::find($id);

$id can either be a string representation of the document id or an instance of MongoId.

Adding requirements

There are 3 types of requirements. Validators, filters and special.

Validators

To use a validator add a requirement with the prefix 'Validator:' followed by the name of the validator. Please see the Zend reference guide for the list of Zend validators. I addition to the validators supported by Zend, Shanty Mongo supports the following validators:

  • Validator:Array

  • Validator:MongoId

Filters

To use a filter add a requirement with the prefix 'Filter:' followed by the name of the filter. Please see the Zend reference guide for the list of Zend filters.

Requirements with special meaning or behaviour

  • Document:{ClassName}
    Validates a property is of type ClassName and lazy loads an instance of the document on first access. If no ClassName is provided then Shanty_Mongo_Document is assumed. eg 'Document:User' or without a class name 'Document'.

  • DocumentSet:{ClassName}
    Validates a property is of type ClassName and lazy loads an instance of the documentset on first access. If no ClassName is provided then Shanty_Mongo_DocumentSet is assumed. eg 'DocumentSet:Posts' or without a class name 'DocumentSet'.

  • Required
    Ensures that a property exists. Unlike most validators that run when a property is set, Required is run when a document is saved.

  • Ignore
    Will prevent a property/field being saved to Mongo. Allows overriding export() function and adding own computed data without persisting back to db.

  • AsReference
    Will save a document as a reference

Lets put some of them to use

class User extends Shanty_Mongo_Document 
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
	
	protected static $_requirements = array(
		'name' => 'Required',
		'email' => array('Required', 'Validator:EmailAddress'),
		'friends' => 'DocumentSet',
		'friends.$' => array('Document:User', 'AsReference')
	);
}

There is a lot going on here so i don't expect you to understand what is happening just yet.

Even though there are 4 keys in the requirement list we are actually only specifying requirements for 3 properties. The last two 'friends' and 'friends.$' both refer to the 'friends' property.

We have enforced that both the properties 'name' and 'email' are required while 'friends' is optional. We have also stated that the 'email' property must be an email address. When an attempt to set the 'email' property is made, the value will be run through the validator Zend_Validate_EmailAddress. If it fails validation an exception will be thrown. If you wanted to determine if an email address is valid without throwing an exception call $user->isValid('email', 'invalid@email#address.com');

The property 'friends' is a document set and all it's elements are documents of type 'User'. When this document set is saved all the 'User' documents will be saved as references. More on document sets later.

Validators and Filters with options

Some validators and filters have additional options that need to be passed to it's constructor. This can be achieve by setting the requirement as the key and the options as the value. As a demonstration we'll add a sex property on the user object and use the InArray validator.

class User extends Shanty_Mongo_Document 
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
	
	protected static $_requirements = array(
		'name' => 'Required',
		'email' => array('Required', 'Validator:EmailAddress'),
		'friends' => 'DocumentSet',
		'friends.$' => array('Document:User', 'AsReference'),
		'sex' => array('Validator:InArray' => array('female', 'male');
	);
}

Creating embedded documents

Say we wanted to also store the users last name. We could have nameFirst and nameLast properties on the document but in the spirit of document databases we'll make the property 'name' an embedded document with the properties first and last.

$user = new User();
$user->name = new Shanty_Mongo_Document();
$user->name->first = 'Bob';
$user->name->last = 'Jane';
$user->save();

Since we know all users must have a first and last name lets enforce it

class User extends Shanty_Mongo_Document 
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
	
	protected static $_requirements = array(
		'name' => array('Document', 'Required'),
		'name.first' => 'Required',
		'name.last' => 'Required',
		'email' => array('Required', 'Validator:EmailAddress'),
	);
}

Notice how i've given the property 'name' the requirement of 'Document'? Now we do not have to initialise a new document when we set a users name. The name document is lazy loaded the first time we try to access it.

$user = new User();
$user->name->first = 'Bob';
$user->name->last = 'Jane';
$user->save();

Saving embedded documents

A nice feature is the ability to save embedded documents independently. eg.

$user = User::find($id);
$user->name->last = 'Tmart';
$user->name->save();

The above example may be a bit pointless but as your documents grow it will feel 'right' to call save on the document you are changing. It's also handy for when you want to pass embedded document around your application without having to remember where they came from.

No matter where save is called only the changes for that document and all it's children are sent to the database.

Custom embedded document classes.

Now that we have stored the users first and last names, more than likely will will want to display the users full name. Instead of concatenating the users first and last name every time, we can make 'name' a custom document with a full() method.

First we'll define the name document

class Name extends Shanty_Mongo_Document
{
	protected static $_requirements = array(
		'first' => 'Required',
		'last' => 'Required',
	);
	
	public function full()
	{
		return $this->first.' '.$this->last;
	}
}

Next we'll tell the user document to use our new document

class User extends Shanty_Mongo_Document 
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
	
	protected static $_requirements = array(
		'name' => array('Document:Name', 'Required'),
		'email' => array('Required', 'Validator:EmailAddress'),
	);
}

Now lets use our new document

$user = User::find($id);

// prints 'Bob Jane'
print($user->name->full()); 

// You could also add a __toString() method and do something like this
print($user->name);

DocumentSets

Document sets are actually documents themselves but designed to handle a set of other documents. Think of DocumentSets as an array with extras. You may want to use a DocumentSet to store a list of friends or addresses.

Lets store a list of addresses against a user. First we must inform the User document of our new requirements

class User extends Shanty_Mongo_Document 
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
	
	protected static $_requirements = array(
		'name' => array('Document:Name', 'Required'),
		'email' => array('Required', 'Validator:EmailAddress'),
		'addresses' => 'DocumentSet',
		'addresses.$.street' => 'Required',
		'addresses.$.suburb' => 'Required',
		'addresses.$.state' => 'Required',
		'addresses.$.postCode' => 'Required'
	);
}

First thing you are probably noticing is the $. The $ is a mask for the array position of any document in the set. Requirements specified against the $ will be applied to all elements. In the above example we are enforcing that all document added to the 'addresses' document set have a bunch of properties.

There are few different ways you can use DocumentSets. I'll start with the most common usage.

$user = User::find($id);

$address = $user->addresses->new();
$address->street = '88 Hill St';
$address->suburb = 'Brisbane';
$address->state = 'QLD';
$address->postCode = '4000';
$address->save();

There is a bit of magic going on here. First we create a new address. The new method on a DocumentSet returns a new document, by default it will be a Shanty_Mongo_Document. We do our business then save. Save will do a $push operation on $user->addresses with our new document. This is in my opinion the ideal way to add new elements to a document set. Because we a doing a $push operation we do not run the risk of a confict on indexes

We could have also added the new document to the document set like this

$user = User::find($id);

$address = $user->addresses->new();
$address->street = '88 Hill St';
$address->suburb = 'Brisbane';
$address->state = 'QLD';
$address->postCode = '4000';

// add address to addresses
$user->addresses->addDocument(address);

// Is the same as
//$user->addresses[] = address;

// Or we could have specified the index directly if we really knew what we were doing
// $user->addresses[4] = address;

$user->addresses->save();

This method may be preferred in certain circumstances

Fetching multiple documents

We can fetch multiple documents by calling all. All will return a Shanty_Mongo_Iterator_Cursor that has all the functionality of MongoCursor

Find all users and print their names

$users = User::all();

foreach ($users as $user) {
	print($user->name->full()."<br />\n");
}

All also accepts queries.

Find all users with the first name Bob

$users = User::all(array('name.first' => 'Bob'));

Just as with finding a single document you can limit the fields that Shanty Mongo will pull down.

$users = User::all(array(), array('name' => 1, 'email' => 1);

This will return only the name and email address for all users.

Using Skip, Limit, Sort etc

Since the shanty mongo cursor returned by the all method is a subclass of MongoCursor you have all the functionality that is usually available to you as if you were querying mongodb directy. eg

$users = User::all()->skip(10)->limit(5);

This will skip the first 10 users and limit the result set to 5 users. Even though it may appear as though we are fetching all the users then skipping and limiting the result set on the php end, this is not the case. The nice thing about the way the Mongo implements cursors is that no results are fetched from the database until the method getNext is called, directly or indirectly. This means that the above skip and limit will only fetch 5 users from the database.

Deleting documents

To delete a document simply call the method delete(). You can call delete() on root documents or embedded documents. eg

$user = User::find($id);

// Delete the name document
$user->name->delete();

// Delete the entire document
$user->delete();

Deleting from a collection

Maybe you just want to delete all users with the first name John without fetching and initialising all the John documents

User::remove(array('name.first' => 'John'));

If you would like that operation to be safe remember to pass the safe flag

User::remove(array('name.first' => 'John'), array('safe' => true));

Batch Inserting

Sometimes you just want to save a whole bunch of stuff to the database without the extra overhead of initialising documents.

$users = array(
    array(
        'name' => array(
            'first' => 'John',
            'last' => 'Mackison'
        ),
        'email' => '[email protected]'
    ),
    array(
        'name' => array(
            'first' => 'Joan',
            'last' => 'Mackison'
        ),
        'email' => '[email protected]'
    )
);

User::insertBatch($users);

This will insert two users into the user collection. A word or warning; batch inserting bypasses all validation and filtering.

Operations

Operations are queued until a document is saved.

Lets increment a users post count by one

$user = User::find($id);

$user->inc('posts', 1);
$user->save();

// Is the same as
$user->addOperation('$inc', 'posts', 1);
$user->save();

Operations also work fine on subdocuments

$user->name->addOperation('$set', 'first', 'Bob);
$user->name->save();

// This would also work
$user->save();

Inheritance

As of 0.3 Shanty Mongo supports inheritance

Class User extends Shanty_Mongo_Document
{
	protected static $_db = 'lms';
	protected static $_collection = 'user';
	protected static $_requirements = array(
		'name' => array('Document:Name', 'Required'),
		'email' => 'Validator:EmailAddress'
	);
}

Class Student extends User
{
	protected static $_requirements = array(
		'email' => 'Required',
		'classes' => 'DocumentSet'
	);
}

Class SchoolCaptain extends Student
{
	protected static $_requirements = array(
		'obligations' => 'Array'
	);
}

In the above User, Student and SchoolCaptain will be saved in the user collection. Even though it looks like the requirements in User are being over-ridden by the requirements in Student and SchoolCaptain but they are not. Using some static magic they are actually merged.

So the effective requirements for SchoolCaptain would be:

array(
	'name' => array('Document:Name', 'Required'),
	'email' => array('Required', 'Validator:EmailAddress'),
	'classes' => 'DocumentSet',
	'obligations' => 'Array',
);

Querying for subclasses is easy

$users = User::all(); // Returns all Users

foreach ($users as $user) {
	print(get_class($user)); // Will print either User, Student or SchoolCaptain
}

Student::all(array('name.first' => 'Bob')); // Returns only Students with the first name of 'Bob'
SchoolCaptain::all(); // Returns only school captains

Before you jump in and use inheritance all over the place just be aware that searching subclasses will query the attribute '_type' so be sure to index it for use in production.

$users = User::all(); // No lookup on '_type'
$students = Student::all(); // A lookup on '_type' is used
$schoolCaptains = SchoolCaptain::all(); // A lookup on '_type' is used

Hooks

The following hooks are available:

init()

Executed after the constructor has finished

preInsert()

Executed before saving a new document

postInsert()

Executed after saving a new document

preUpdate()

Executed before saving an existing document

postUpdate()

Executed after saving an existing document

preSave()

Executed before saving a document

postSave()

Executed after saving a document

preDelete()

Executed before deleting a document

postDelete()

Executed after deleting a document

Using the Hooks

To use one of the above hooks simply define a protected method in you document with the name of the hook eg.

Class User extends Shanty_Mongo_Document
{
	protected static $_db = 'forum';
	protected static $_collection = 'user';
	
	protected function init()
	{
		// Do stuff on initialising document
	}
}

Running Tests

Shanty has good test coverage. It's easy to run the tests:

  • Run composer install --dev
  • Run ./vendor/bin/phpunit

If needed, you can change the MongoDB connection string, or the database name by editing the phpunit.xml.dist file.

All tests should pass!

Community

Shanty Mongo Google Group

Project Organisers

tholder

jwpage

settermjd

coen-hyde

Special thanks to

tonymillion

stunti

sh for Shanty_Paginator

Mongoid for inspiration

shanty-mongo's People

Contributors

chrismytton avatar coen-hyde avatar jwpage avatar markosamuli avatar rakesh-sankar avatar stunti avatar tholder avatar tonymillion avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shanty-mongo's Issues

Hooks not firing when parent document is saved

First of all, fantastic work. Very simple, very natural.

Just noticed one thing, not really sure this is an issue as such, just slightly confusing about the way it works.

If I have classes defined as:

array('Document:Name', 'Required') ); } class Name extends Shanty_Mongo_Document { ``` protected function preSave() { $this->fullName = $this->forename . ' ' . $this->surname; } ``` } ?>

When I create a new contact I want it to build the full name using the preSave hook.

Doing this:

name->forename = 'Tom'; $c->name->forename = 'Surname'; $c->save(); ?>

That doesn't trigger the hook.

This however does:

name->forename = 'Tom'; $c->name->forename = 'Surname'; $c->name->save(); $c->save(); ?>

I might look at forking this to see if it's possible to change it, but I'm not entirely sure this is the wrong behavior.

Also, one question, can you get MongoID of the parent from within a child document?

Thanks
Tom

FetchOne will not set mongoId from Database

I am trying to find the the mongoId that is stored with a document, however, since the Document constructor creates a new Shanty_Mongo_Document which creates a new mongoId, the correct value from the database will not get set once a record is found.

Record:
{ "_id" : ObjectId("4e85e986c2e493040a000003"), "created" : 1317398918, "last_modified" : 1317398918, "email" : "[email protected]", "first_name" : "Chuck", "last_name" : "Reeves", "password" : "ba2034c7fd9dde0ba4ca839ebd3aa77d" }

PHP:

class MTM_Model_Account
    extends Shanty_Mongo_Document
{
//... snip
    public function login($email, $password)
    {
        $check = $this->fetchOne(array('email' => $email, 'password' => self::encryptPassword($password)));

        if ($check == null) {
            return false;
        }

        die($check->id);

        return true;
    }
}

Actual:
4e85ebeac2e493a817000006

Expected:
4e85e986c2e493040a000003

I can's save document in document set

Hi!

I write this code:
class Application_Model_User extends Shanty_Mongo_Document
{
protected static $_dbName = 'fin';
protected static $_collectionName = 'user';

 protected $_requirements = array(
    'once' => 'DocumentSet',
    'weekly' => 'DocumentSet',
    'monthly' => 'DocumentSet');

}

And this:
$amount = $this->user->once->new();
$amount->name = 'blah-blah';
$amount->save();

When I execute this code i have an error:
"Shanty_Mongo_Document::$_dbName is null."

A minor issue with the Int validator

The Int validator doesn't differentiate these two entries: "123" and 123. However, mongodb consider the them as string and integer respectively.

So an (integer) typecast can be applied before applying the Zend_Validate_Int

Bug: "save" should be "safe" in /library/Shanty/Mongo/Document.php on line 928.

This seems to be a bug:

/library/Shanty/Mongo/Document.php:928:

$result = $this->_getMongoCollection(true)->update($this->getCriteria(), $operations, array('upsert' => true, 'save' => $safe));

should be changed to:
$result = $this->_getMongoCollection(true)->update($this->getCriteria(), $operations, array('upsert' => true, 'safe' => $safe));

Otherwise it will always be save in unsafe mode and never throw an exception.

CollectionTest: testUpdate/testRemove failing

Hello,

the collection update and remove tests are failing for me:

  1. Shanty_Mongo_CollectionTest::testUpdate
    Failed asserting that two strings are equal.
    --- Expected
    +++ Actual
    @@ @@
    -Lauren
    +Cherry

CollectionTest.php:410

The record seems to get updated/removed after the find. Doing a safe update/remove passes the test.

My_ShantyMongo_User::update(array('_id' => new MongoId('4c04516f1f5f5e21361e3ab1')), array('$set' => array('name.first' => 'Lauren')), array('safe' => true));
My_ShantyMongo_User::remove(array('name.first' => 'Bob'), array('safe' => true));

Are the tests passing for you?

how to use MongoDBRef::create

i've a model:

class Application_Model_Db_Table_ProfileAttribute extends Shanty_Mongo_Document
{
protected static $_db = 'test';
protected static $_collection = 'profileAttribute';

protected static $_requirements = array(
    'attributeGuid' => array('Document:Application_Model_Db_Table_Attribute')
);

}

i try this:

    $a = Application_Model_Db_Table_Attribute::one(array('guid'=>'fixedTitle'));

    $data = array(
            '_type' => array(
                'Application_Model_Db_Table_ProfileAttribute',
                'Application_Model_Db_Table_Attribute'
            ),
        'profileGuid' => 'article',
        'attributeGuid' => MongoDBRef::create('Application_Model_Db_Table_Attribute', new MongoId($a->getId())),
        'defaultValues' => '',
        'viewOrder' => 1
    );

    Application_Model_Db_Table_ProfileAttribute::insert($data, array('safe' => true));

result:

        [attributeGuid] => Array
            (
                [$ref] => Application_Model_Db_Table_Attribute
                [$id] => MongoId Object
                    (
                        [$id] => 4e9fa6b1ecb22fa00100009c
                    )

            )

question:

how do i get $id, coz i try print_r($result->attributeGuid) it gave me NULL ?

Comparisons on 'stuff'

I have a blog system with 'User' objects. Users can either post or comment. A poster can comment on their own post.

In both posts and comments I have

class Comment extends Shanty_Mongo_Document
{
    protected static $_requirements = array(
        'user' => array('Document:User', 'AsReference'),


class Post extends Shanty_Mongo_Document 
{
    protected static $_requirements = array(
        'user' => array('Document:User', 'AsReference'),
        'comments' => 'DocumentSet',
        'comments.$' => 'Document:Comment',

in my code I was coding a system that sends a notification to the writer of a Post and to all the Commenters on a Post. Obviously if a poster has commented on their post I don't want them getting two notifications so I did something like:

foreach($post->comments as $comment)
{
     if($post->user == $comment->user)

however the comparison fails … if I do

 if($post->user->getID() == $comment->user->getID())

it succeeds.

Is this correct operation? Am I doing something wrong?

Auto Loading Troubles

I'm having trouble getting the classes correctly loaded by the Autoloader. When I specifically register the Shanty_ namespace it loads the Connection class but then it can't find the 'Mongo' class.

Zend_Loader_Autoloader::getInstance()->registerNamespace('Shanty_');

When I don't register the namespace it can't find 'Shanty_Mongo_Connection'. I have the 'Shanty' directory inside my library directory (right next to the Zend directory). I've confirmed the library path is part of my include path, so I'm not sure why the classes aren't loading.

Issue updating document set of existing doc

Trying to create a document with a document set.

There is a unique key on the email field though, if a dupe is detected whilst saving, I want to take the contents of the documentset and save it against the existing row.

Originally I did just:

//Save it to existing document.
$i = Model_Mongo_Invite::one(array('email' => $this->email));
$i->companies - $this->companies;
$i->save();

That didn't work, it created two documents like:

{ "_id" : ObjectId("4e88794093fd9afc1c000000"), "_type" : [ "Model_Mongo_Invite" ], "companies" : [
{
"company" : {
"$ref" : "company",
"$id" : ObjectId("4e88607993fd9ab31a070000")
},
"addedByUser" : {
"$ref" : "user",
"$id" : ObjectId("4e88607993fd9ab31a060000")
},
"createdAt" : 1317566784
}
], "email" : "[email protected]", "secret" : "MTQxZWQ0MWViNGIwYzY4MzQ0MGU3NzQxYmJjNjNjNjU" }
{ "_id" : ObjectId("4e88794293fd9afc1c010000"), "companies" : [
{
"company" : {
"$ref" : "company",
"$id" : ObjectId("4e88607993fd9ab31a070000")
},
"addedByUser" : {
"$ref" : "user",
"$id" : ObjectId("4e88607993fd9ab31a060000")
},
"createdAt" : 1317566786
}
] }

I assumed this didn't work at first because the old documentset was still referencing the other object, so I converted to the following but got the same error:

See https://gist.github.com/1257513 for code of the classes.

Collections inside Document "toArray" function

I use the Shanty_Mongo_Document to extend my Model Classes.
I wondered if a function exists like "toArray()"

I saw you store the raw array in your protected var "_cleanData"

For the moment i have to iterate through every object to gain the information i need.
But for the REST webservice i develop it would be enough to response the raw data.

Any help would be nice.

Connect to 2 different Mongodb Servers

Just want to ask, what if i want to connect to 2 different mongodb servers? For example, i need to connect to mongodb server that stores user identity, and connect to other mongodb server that stores something else...

"Pure" arrays not accessible in a Document

I am not sure if this is a bug or not.

I tried storing a "pure" array in a Document, like this:

$doc = Shanty_Mongo_Document:.create();
$doc->val = array('a','b','c');
$doc->save();

When I try to reload it, I get an error:

$doc = Shanty_Mongo_Document::find(...);
print implode(',', $doc->val);

Warning: implode(): Invalid arguments passed...

print_r($doc->val);

Shanty_Mongo_Document Object ( [_docRequirements:protected] => Array ( [_id] => Array ( [Validator:MongoId] => ) [_type] => Array ( [Array] => ) ) [_data:protected] => Array ( ) [_cleanData:protected] => Array ( 0 => 'a', 1 => 'b', 2 => 'c') ...

It seems that it transforms the array in a sub-document, but only during __get, as if I print_r($doc) everything seems fine. Browsing the source (0.3.0) I found this in Shanty_Mongo_Document (row 562):

if ($this->hasRequirement($property, 'Array')) {
return $this->_data[$property] = $data;
}

that leads me to thinking that I can force a requirement Array in the class, so I did it:

protected static $_requirements = array('val'=>'Array');

but then I get an exception:

Shanty_Mongo_Exception: No requirement exists for 'Array'

I solved it modifying Shanty_Mongo, adding on line 41:

static::storeRequirement('Array', new Shanty_Mongo_Validate_StubTrue());

This way everything works well (reading, writing, etc), or so it seems. :)
Is my patch correct or did you have something else in mind?

Issues with AsReference inside a DocumentSet

I accidentally closed the previous ticket I opened so I just copy/pasted it here. Sorry about that, my first time using GitHub :)

Thanks for your work on this Coen. This is going to be a lifesaver for me. I am having an issue that is probably similar to Issue #8 (DocumentSet of References) except I have a DocumentSet with one of the fields AsReference.

If I remove the AsReference from line 12, then the user info is embedded nicely within the post. When referenced however, I cannot retrieve the user data. The body and datetime of the comment still save fine though.

My code is pastied below. Thoughts?

http://pastie.org/1422624

Custom validator

Hi!

Can I use my custom validators? As I see it searches validators in Zend_Validate..., so i can not use my own one if it's not in this namespace. Or am I wrong and there's another way? It would be very useful.
Thanks
blyan

Error:Can not create reference. Document is not a root document

Hi Coen,
I am running into this problem and not sure why. I am basically calling a new document and adding a reference to the user document. However, I am getting the following error:

Can not create reference. Document is not a root document

$user = User::find($id);

$task->assignees[] = $user;
$task->save();

Any help is appreciated.

Thanks,
Ben

Calling delete on embeded document leaves null behind

I'm being left with a null behind when I call delete on an embeded document.

I converted it to this to get around the problem:

My_Document::update(array('_id' => $document->getId()), array('$pull' => array('applications' => array('application.$id' => $this->app->getId()))));

Saves are always unsafe

Currently there is no way to set the "save" option when updating a Document.
There for e.g. unique index violations are silently ignored.

Keep up the great work !
Best regards
Bret

Updating or inserting depending on existing data

What's the best way to go about doing the equivalent of a MySQL REPLACE (not that I especially liked that!).

At the moment I'm doing similar to:

$data = array(
'user' => $app->user->getId(),
'key' => $keyValue['key']
);

        $appData = Model_Mongo_UserData::one($data);
        if(!$appData) {
            $appData = new Model_Mongo_UserData($data);
        }

Replacing complete DocumentSet

It's not very clear how you go about clearing and re-setting a documentset on an existing document.

Scenario is I have a document that has a documentset of categories. Each time I update the document I want to completely replace this field with the newly selected options. The only way I have found to successfully do this (every other way seems to just add the document) is to do the following:

$doc->name = $this->_getParam('name');
$doc->category = new Shanty_Mongo_DocumentSet();
$doc->category->addDocument($cat1);
$doc->category->addDocument($cat2);
$doc->save();

If this is the correct procedure I will add something to the wiki but is it worth putting a clearAll() method or something on to the DocumentSet object?

Question/Suggestion: Unique Index Recognition

First off - I really appreciate ShantyMongo, thanks for developing it!

I notice that when I create and save a document with a property that has a unique index, Shanty_Document's save() method returns true, whether or not that property is a duplicate. I wouldn't necessarily say this is a bug, but I feel like it should return false when it fails to save.

Secondly, I notice that trying to set a property that is not valid throws an exception. This is great, except that it means that property's value must get validated twice - once before setting the property and again while setting the property. To remove this redundancy, it would make sense for setProperty() return true when valid and false when invalid, and then have a method getErrors() that you could optionally call after it returns false to find the specific error if you needed to.

So with that said, it's possible that I've overlooked a config option somewhere that does as mentioned - I am new to both MongoDB and ShantyMongo.

Chris

DocumentSet of references

Hello
It seems there is a small bug in DocumentSet class: it does not allow to save collections of references. So if you declare a model from the example:

class User extends Shanty_Mongo_Document
{
protected static $_db = 'forum';
protected static $_collection = 'user';

protected static $_requirements = array(
    'name' => 'Required',
    'email' => array('Required', 'Validator:EmailAddress'),
    'friends' => 'DocumentSet',
    'friends.$' => array('Document:User', 'AsReference')
);

}

and add new users:

$user = new User();
$user->name = 'Bob';
$user->email = '[email protected]';
$friend = User::find($id);
$user->friends[] = $friend;
$user->save();

, 'friends' property will contain copies of specified User objects, not references to them.
As far as I can see the problem is that export operation does not know about collection item requirements. So a quick way I found to fix that is to add the following code to line 171 in \library\Shanty\Mongo\DocumentSet.php :

    else    {
        $this->applyRequirements(array($index => 'AsReference'));
    }

Is there any way to fix this problem better or I simply make some mistake during my tests?

PHP Parse error

I get

PHP Parse error:  syntax error, unexpected T_STATIC, expecting T_STRING or T_VARIABLE or '$' in /srv/vhosts/dev/sites/ombufiles/library/Shanty/Mongo.php on line 28

line 28 looks like this:

if (!empty(static::$_requirements)) return;

using
PHP: 5.2.10-2ubuntu6.47
mongodb adapter: 1.0.8

Finding DocumentSet

From the examples:
$address = $user->addresses->new();
$address->street = '88 Hill St';
$address->suburb = 'Brisbane';
$address->state = 'QLD';
$address->postCode = '4000';

// add address to addresses
$user->addresses->addDocument(address);

Now how would I find all of the addresses with the postCode 4000?

In a similar situation I am trying $user->address->find( array('postCode' => 4000)); and get the error message: Error:Shanty_Mongo_DocumentSet::$_collection is null

Am I going about the wrong way of querying this?

Thanks,
Ben

sometimes unauthorized connection

Application error

Exception information:

Message: unauthorized db:plants lock type:-1 client:xxx.xxx.xxx.xxx

Stack trace:
#0 /srv/bga/https/zend/library/Shanty/Mongo/Iterator/Cursor.php(125): MongoCursor->rewind()
#1 /srv/bga/https/zend/library/Service/Ticket.php(58): Shanty_Mongo_Iterator_Cursor->rewind()
#2 /srv/bga/https/zend/application/controllers/TicketController.php(20): Service_Ticket->getGroups('4e3138fb818f94b...')
#3 /srv/bga/https/zend/library/Zend/Controller/Action.php(513): TicketController->indexAction()
#4 /srv/bga/https/zend/library/Zend/Controller/Dispatcher/Standard.php(295): Zend_Controller_Action->dispatch('indexAction')
#5 /srv/bga/https/zend/library/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#6 /srv/bga/https/zend/library/Zend/Application/Bootstrap/Bootstrap.php(97): Zend_Controller_Front->dispatch()
#7 /srv/bga/https/zend/library/Zend/Application.php(366): Zend_Application_Bootstrap_Bootstrap->run()
#8 /srv/bga/https/zend/public/index.php(33): Zend_Application->run()
#9 {main}

Request Parameters:

array (
'controller' => 'ticket',
'action' => 'index',
'id' => '4e3138fb818f94b174000600',
'module' => 'default',
)

Issues with AsReference inside a DocumentSet

Thanks for your work on this Coen. This is going to be a lifesaver for me. I am having an issue that is probably similar to issue 3 (Cannot define a DocumentSet of References) except I have a DocumentSet with one of the fields AsReference.

If I remove the AsReference from line 12, then the user info is embedded nicely within the post. When referenced however, I cannot retrieve the user data. The body and datetime of the comment still save fine though.

My code is pastied below. Thoughts?

http://pastie.org/1422624

DocumentSet is overwritten?

Dear all,

I'm currently trying to work my way through Shandy Mongo when I ran into an issue. Not sure if it's really an issue or it's just me but I'd give it a try here anyways...

Taking the example:

$user = User::find($id);

$address = $user->addresses->new();
$address->street = '88 Hill St';
$address->suburb = 'Brisbane';
$address->state = 'QLD';
$address->postCode = '4000';

$user->addresses->addDocument(address);
$user->addresses->save();

works fine so far.

If I now "start over" again to add a second address to the user, the debug then only shows me that there is just the second address saves in the DocumentSet, the first initial one has disappeared which looks to me as it would've been overwritten?

Or am I thinking in the wrong direction here somewhere? :-)

Any hints appreciated.

Issue setting AsReference object through property after object serialized

Seems to be a bit of a bug/niggle when setting an object using a property setter. The following works:

            $data = array();
            $data['appName'] = $this->_getParam('appName');
            $data['urlKey'] = $this->_getParam('urlKey');
            $data['owner'] = $this->company;
            $data['createdBy'] = $this->user;

            $app = new Model_Mongo_Application($data);
            $app->save();

The following doesn't:

            $app = new Model_Mongo_Application();
            $app->appName = $this->_getParam('appName');
            $app->urlKey = $this->_getParam('urlKey');
            $app->owner = $this->company;
            $app->createdBy = $this->user;
            $app->save();

It produces:

Shanty_Mongo_Exception: Property 'owner' must not be null. in /Users/tholder/Sites/listenablefe/library/Shanty/Mongo/Document.php on line 805

Model is defined with requirements like:

protected static $_requirements = array(
    'appName' => array('Required'),
    'urlKey' => array('Required'),
    'owner' => array('Document:Model_Mongo_Company', 'AsReference', 'Required'),
    'createdBy' => array('Document:Model_Mongo_User', 'AsReference', 'Required')
);

Document set containing document with DocumentSet

This may not be a bug but something is up with it.

If you have a document structured like:

company
--->categories [DocumentSet]
--------->category
---------------->access [DocumentSet]
----------------------->access

You can't do this:

$c = $company->categories->new();
$c->name = 'My Cat';
$c->save();

$a = $c->access->new();
$a->hasAccess = true;
$a->save();

When calling second save I get:

MongoCursorException: can't append to array using string field name [access] in on line 948

It's not clear the correct order in which to be saving things. I have, accidentally, managed to get it saving correctly once but I've just lost the correct combination. Will add it when I find it.

Get parent of subdocument

Hi,

I retrieve a Document (Parent) from the database and it contains a DocumentSet. I run through this DocumentSet and pass its elements (let's call them Subs) it on to other classes and methods. In some of these methods, I need the Parent document.

I found a method called isParentDocumentSet, but no method called anything like getParent. If I can't get the Parent directly from the Sub, I would have to pass the parent along with the Sub to all these methods, which I don't feel much for.

Is it possible to get the parent document directly from a sub-document? If not, might that be a good new feature?

Thanks.

How to use replicaSet

Could you please post some examples of the replicaSet.
Here's is what I've tried:

    $connections = array(
        'hosts' => array(
            array(
                'host' => 'server1',
                'username' => 'USERNAME',
                'password' => 'PASSWORD',
            ),
            array(
                'host' => 'server2',
            ),
            array(
                'host' => 'server3'             
            )
        ),
        'replicaSet' => true
    );

    $group = new Shanty_Mongo_Connection_Group($connections);

Instantiating correct concrete classes in constructor

There seems to be an issue when an object is instantiated with an array of data.

The sub data/document elements are not being initialised as the correct objects, meaning you obviously lack functionality in those rich embeded objects you might need in preInsert etc.

My solution is currently to do something like the following in the contructor, but, in my class level it feels a bit dirty! Should shanty be doing something similar lower down?

<?php
class Model_Mongo_Contact extends Shanty_Mongo_Document {

    protected static $_collection = 'contact';

    protected static $_requirements = array(
        'poco' => array('Document:Model_Mongo_Poco', 'Required')
    );

    /**
     * Bit of a nasty hack, but fixes issue with Shanty where setting array in 
     * constructor doesn't correctly setup child object. This causes preInsert to fail.
     *
     * @return void
     * @author Tom Holder
     **/
    public function __construct($data = array(), $config = array())
    {
        parent::__construct($data, $config);

        //We need to make sure the poco element of the incoming data is a concrete ponco class.
        if(array_key_exists('poco', $data) && is_array($data['poco'])) {
            $this->poco = new Model_Mongo_Poco($data['poco']);
        }
    }

Filter issue

What am I doing wrong with this filter?

array('Filter:Zend_Filter_StringTrim') ); ``` .. I keep getting "No requirement exists for 'Filter:Zend_Filter_StringTrim'" thrown. I've tried adding in 'Required' and that doesn't seem to work. The documentation is great but a little sparse on Filters so perhaps I'm justing being stupid! Thanks Tom

Setter for $_db var

Hi,

Could there be public set method for defining protected static $_db ?
Llike setDb($db) ?

I would like to store db name in my config and set it once for all my
document types. And of course with this method I could use other dbs
also.

br, Marko

Problem saving DocumentSet Reference

Great work on Shanty Mongo. My team has been enjoying checking it out and working with it.

I did run into a bit of a snag though. I am getting the following error when trying to save a documentset as a reference.

zero-length keys are not allowed, did you use $ with double quotes?

My guess is that it doesn't like the array going in for assignees.$. When looking at the object before calleing save() it looks like it is an indexed array.

Here are the code snippets that I am using. The ->save() is throwing the error. It could just be that I'm missing something simple.

protected static $_requirements = array(
'name' => 'Document',
'assignees' => 'DocumentSet',
'assignees.$' => array('Document:User', 'AsReference'),
'creator' => array('Document:User','AsReference')
);

$data = array();
$data['name'] = 'Bob';
$data['creator'] = $user;
$data['assignees'][] = $user;

$newTask = new Task($data);
$newTask->save();

Thanks,
Ben

DocumentSet saved as sub-document, not array

Hi Coen,

First of all: Awesome work - thanks for saving me a lot of time developing my application!

As I am still pretty new to this stuff, I hope that this post is not redundant (and I didn't just make some stupid mistake).

I noticed this when trying to do some native MongoDB operations on arrays and, after playing around with it for a while, came to the following conclusion:

Assuming you have created a new Document, saved it and are now adding items to a DocumentSet of that Document:

$document->documentSet->addDocument($doc1);
$document->documentSet->addDocument($doc2);

Then the operation

$document->save();

will save the DocumentSet as an array ([ $doc1, $doc2 ]), as is probably the intention, whereas

$document->documentSet->save();

saves the DocumentSet instead as a sub-document with the numeric array indexes as attribute keys ({ "0": $doc1, "1": $doc2 }).

I couldn't figure out what the reason for this is, but I guess that somehow the way the save() function works when called directly on the DocumentSet makes MongoDB think you wanted to save the PHP array as a document and not an array in the DB.

Below is part of the source code I have been using - note that I am only saving references to the DocumentSet, haven't tried it with full documents.

Again, thanks for your support!

Dennis

Requirements for document User which is derived from Shanty_Mongo_Document:

protected static $_requirements = array(
[...]
'courses' => 'DocumentSet',
'courses.$' => array('Document:Course', 'AsReference'),
);

Creating the document and document set:

$model = User::create(array(
'firstname' => 'Tester',
'lastname' => 'Testing',
'email' => '[email protected]',
));
$model->save();
$course = Course::find($id1);
$model->courses->addDocument($course);
$course = Course::find($id2);
$model->courses->addDocument($course);
// option 1:
$model->save();
// option 2, yielding different results:
// $model->courses->save();

MongoDB shell output for option 1:

{ "_id" : ObjectId(" "), "_type" : [ "User" ], "courses" : [
{
"$ref" : "courses",
"$id" : ObjectId(" ")
},
{
"$ref" : "courses",
"$id" : ObjectId(" ")
}
], "email" : "[email protected]", "firstname" : "Tester", "lastname" :
"Testing" }

Output for option 2:

{ "_id" : ObjectId(" "), "_type" : [ "User" ], "courses" : {
"0" : { "$ref" : "courses", "$id" : ObjectId(" ") },
"1" : { "$ref" : "courses", "$id" : ObjectId(" ") } }, "email" : "[email protected]", "firstname" : "Tester",
"lastname" : "Testing" }

Array Element by Position ?

i've a data :
"relatedItem" : { "0" : { "itemGuid" : "10" }, {"1" : {"itemGuid" : "20" }

I tried:

  • $catalog->relatedItem->find(array('itemGuid':'20')); and get the error message:
    Error:Shanty_Mongo_DocumentSet::$_collection is null
  • $catalog->find(array('relatedItem' => array('$elemMatch' => array('itemGuid': '20'))));
    null
  • $catalog->find(array('relatedItem' => array(array('itemGuid': '20'))));
    null
  • $catalog->find(array('relatedItem.1.itemGuid'=>'20'));
    20 ----> it's not a standard query syntax

how to match an entire document within the relatedItem array ?

thanks, I appreciate the quick response

DocumentSet Saving a new document doesn't appear to add it to the collection

I ran into this strange behavior, both of these examples update the database correctly, however only the second acts correctly if the DocumentSet is iterated after adding the vote. The document leads me to understand that $address->save() will act magically like ->addDocument($address), but doesn't appear to do so. (Note: This is the first document added to the DocumentSet)

Case 1 - data will be written to the database and is available after roundtrip, however iterating $user->addresses after saving the address will not contain the added address:

$address = $user->addresses->new();
$address->street = '88 Hill St';
$address->save()
$count = $user->addresses->count()
$user->save();

Result : $count = 0;

Case 2 - data will be written to the database and is available immediately iterating $user->addresses

$address = $user->addresses->new();
$address->street = '88 Hill St';
$user->addresses->addDocument($address);
$count = $user->addresses->count()
$user->save();

Result : $count = 1;

Custom Validators

I can't find an easy way to add custom validators. This would be very useful though.

Thanks

Removing References

What is the best way to remove a Document Reference using Shanty-Mongo?

Thanks,
Ben

addToSet

Hi

I have a web app which has a tag cloud, all tags are converted to lower case and put in an array, eventually I'd like to execute code like:

{ $addToSet : { tags : { $each : [ 'word1', 'word2', 'word3'] } } }

I have tried iterating over the array calling

$post->addToSet('tags', strtolower($value));

for each element, but it only ever sets the last one (and of course $each:[] does not work.

Is it possible to actually do this, and if not can it be added?

Collection all() failing

class Application_Model_Db_Table_Post extends Shanty_Mongo_Document
{
protected static $_db = 'holemp';
protected static $_collection = 'user';
}

$user = Application_Model_Db_Table_Post::all();

why not work on me ?

output:
Shanty_Mongo_Iterator_Cursor Object
(
[_cursor:protected] => MongoCursor Object
(
)

[_config:protected] => Array
    (
        [connectionGroup] => default
        [db] => holemp
        [collection] => user
        [documentClass] => Application_Model_Db_Table_Post
        [documentSetClass] => Shanty_Mongo_DocumentSet
    )

)

$pull from documentset

Hi
I have following structure in my collection:
"laboratory" : {
"mixtures" : [
{
"id" : ObjectId("4ef0ede2948b80fe1fb591d3"),
"name" : "ZZZ"
}
],
}
and I want to pull this mixture. I tried
$this->doc->pull('laboratory.mixtures',array('id'=>new MongoId('4ef0ede2948b80fe1fb591d3')));
$this->doc->laboratory->mixtures->pull('id',array(new MongoId('4ef0ede2948b80fe1fb591d3')));

but it is not working. Why? This code works but it is not clean way:
Model_Collections_Users::update(array('_id'=>$this->doc->_id),
array('$pull'=>array('laboratory.mixtures'=> array('id'=>new MongoId('4ef0ede2948b80fe1fb591d3')))));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.