PHPackages                             coen-hyde/shanty-mongo - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. coen-hyde/shanty-mongo

AbandonedArchivedLibrary[Database &amp; ORM](/categories/database)

coen-hyde/shanty-mongo
======================

Shanty Mongo is a mongodb library for the Zend Framework.

19813.9k51[33 issues](https://github.com/coen-hyde/Shanty-Mongo/issues)PHP

Since Jun 17Pushed 8y ago2 watchersCompare

[ Source](https://github.com/coen-hyde/Shanty-Mongo)[ Packagist](https://packagist.org/packages/coen-hyde/shanty-mongo)[ RSS](/packages/coen-hyde-shanty-mongo/feed)WikiDiscussions master Synced 4w ago

READMEChangelogDependenciesVersions (6)Used By (0)

RETIRED
=======

[](#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
============

[](#shanty-mongo)

[![Build Status](https://camo.githubusercontent.com/fde507c56c5b7494461cf493b08e7f67375128788ca7a400140c06b0ab9c5f6c/68747470733a2f2f7365637572652e7472617669732d63692e6f72672f636f656e2d687964652f5368616e74792d4d6f6e676f2e706e673f6272616e63683d6d6173746572)](http://travis-ci.org/coen-hyde/Shanty-Mongo)

Summary
-------

[](#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
------------

[](#requirements)

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

Features
--------

[](#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
----------

[](#how-to-use)

### Initialisation

[](#initialisation)

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

### Connections

[](#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

[](#advanced-connections)

For information on how to configure master/slave setups, weighted connections and multiple connection goups see the [wiki](http://wiki.github.com/coen-hyde/Shanty-Mongo/connections)

### Define a document/collection

[](#define-a-documentcollection)

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

[](#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

[](#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

[](#adding-requirements)

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

#### Validators

[](#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](http://framework.zend.com/manual/en/zend.validate.set.html). I addition to the validators supported by Zend, Shanty Mongo supports the following validators:

- Validator:Array
- Validator:MongoId

#### Filters

[](#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](http://framework.zend.com/manual/en/zend.filter.set.html).

#### Requirements with special meaning or behaviour

[](#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

[](#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-&gt;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

[](#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

[](#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

[](#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.

[](#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

[](#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-&gt;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

[](#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()."\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

[](#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

[](#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

[](#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

[](#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' => 'john@mackison.com'
    ),
    array(
        'name' => array(
            'first' => 'Joan',
            'last' => 'Mackison'
        ),
        'email' => 'joan@mackison.com'
    )
);

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)

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

[](#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

[](#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

[](#hooks)

The following hooks are available:

##### init()

[](#init)

Executed after the constructor has finished

##### preInsert()

[](#preinsert)

Executed before saving a new document

##### postInsert()

[](#postinsert)

Executed after saving a new document

##### preUpdate()

[](#preupdate)

Executed before saving an existing document

##### postUpdate()

[](#postupdate)

Executed after saving an existing document

##### preSave()

[](#presave)

Executed before saving a document

##### postSave()

[](#postsave)

Executed after saving a document

##### preDelete()

[](#predelete)

Executed before deleting a document

##### postDelete()

[](#postdelete)

Executed after deleting a document

#### Using the Hooks

[](#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
-------------

[](#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
---------

[](#community)

[Shanty Mongo Google Group](https://groups.google.com/forum/?fromgroups#!forum/shanty-mongo)

Project Organisers
------------------

[](#project-organisers)

[tholder](http://github.com/tholder)

[jwpage](http://github.com/jwpage)

[settermjd](http://github.com/settermjd)

[coen-hyde](http://github.com/coen-hyde)

Special thanks to
-----------------

[](#special-thanks-to)

[tonymillion](https://github.com/tonymillion)

[stunti](http://github.com/stunti)

[sh](http://github.com/sh) for Shanty\_Paginator

Mongoid for inspiration

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance17

Infrequent updates — may be unmaintained

Popularity39

Limited adoption so far

Community23

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 69.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/3e9c174f9276d256ac0e92c334a7d342a8140ad538138d5275de9e66ac4c92eb?d=identicon)[jwpage](/maintainers/jwpage)

![](https://avatars.githubusercontent.com/u/123503?v=4)[Coen Hyde](/maintainers/coen-hyde)[@coen-hyde](https://github.com/coen-hyde)

---

Top Contributors

[![coen-hyde](https://avatars.githubusercontent.com/u/123503?v=4)](https://github.com/coen-hyde "coen-hyde (109 commits)")[![jwpage](https://avatars.githubusercontent.com/u/52687?v=4)](https://github.com/jwpage "jwpage (20 commits)")[![tholder](https://avatars.githubusercontent.com/u/41235?v=4)](https://github.com/tholder "tholder (18 commits)")[![chrismytton](https://avatars.githubusercontent.com/u/22996?v=4)](https://github.com/chrismytton "chrismytton (3 commits)")[![markosamuli](https://avatars.githubusercontent.com/u/61325?v=4)](https://github.com/markosamuli "markosamuli (2 commits)")[![stunti](https://avatars.githubusercontent.com/u/52714?v=4)](https://github.com/stunti "stunti (2 commits)")[![tonymillion](https://avatars.githubusercontent.com/u/316954?v=4)](https://github.com/tonymillion "tonymillion (1 commits)")[![rakesh-sankar](https://avatars.githubusercontent.com/u/555164?v=4)](https://github.com/rakesh-sankar "rakesh-sankar (1 commits)")

### Embed Badge

![Health badge](/badges/coen-hyde-shanty-mongo/health.svg)

```
[![Health](https://phpackages.com/badges/coen-hyde-shanty-mongo/health.svg)](https://phpackages.com/packages/coen-hyde-shanty-mongo)
```

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k116.5M113](/packages/jdorn-sql-formatter)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8351.6M87](/packages/propel-propel1)[jfelder/oracledb

Oracle DB driver for Laravel

11518.4k](/packages/jfelder-oracledb)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
