[Extension] Mongoyii

The relations by default are not eager, aka they dont load when you find the product.

To access a relation merely do:




foreach($product->offers as $offer){



Resolving relations on the spot, in MongoDB, can be extremely consuming (can’t do server-side matrix joins) so its not done.

Hi!

I need to make a filter by _id for CGridView. For Int field ("__v" in my example) it works, but doesn’t for MongoId field.




class User extends EMongoDocument {

	public function rules() {

		return array(

			array('_id,__v', 'safe'),

			array('_id,__v', 'safe', 'on' => 'search'),

		);

	}


	public function search() {

		$criteria = new EMongoCriteria;

		$criteria->compare('_id', new MongoId($this->_id));

		$criteria->compare('__v', $this->__v);

		return new EMongoDataProvider(get_class($this), array(

			'criteria' => $criteria,

		));

	}

}






class UserController extends Controller {

	public function actionIndex() {

		$model = new User('search');

		$model->unsetAttributes();

		if (Yii::app()->getRequest()->getQuery('User')) {

			$query = Yii::app()->getRequest()->getQuery('User');

			$attributes = array();

			foreach($query as $key => $value) {

				if (!empty($value)) {

					$attributes[$key] = $value;

				}

			}

			$model->attributes = $attributes;

		}

		$this->render('index', array('model' => $model));

	}

}






$this->widget('zii.widgets.grid.CGridView', array(

	'id'		=> 'user_grid',

	'dataProvider'	=> $model->search(),

	'filter'		=> $model,

	'columns'		=> array(

		'_id',

		'__v',

	),

));



I get an error:




MongoException

Cannot run command count(): exception: invalid query 


protected/extensions/MongoYii/EMongoCursor.php(134)


133     public function count($takeSkip = false /* Was true originally but it was to change the way the driver worked which seemed wrong */){

134         return $this->cursor()->count($takeSkip);

135     }



What can be a reason?

Thanks!

I’ll take a look into this in a bit, I am fairly certain I do a search by _id in CGridView in my test repo

Take a look, please.

Order by _id in CGridView works.


User::model()->find(array('_id' => new MongoId('52989dc93c3cdeeafbd974cf')));

works too.

But the filter - doesn’t.

Thanks.

Ok I have figured out why, this is a problem with EMongoCriteria, it creates this query:




array

  '_id' => 

    array

      '$in' => 

        object(MongoId)[31]

          public '$id' => string '529f29986803fab30553c365' (length=24)



Which as you can see is not valid.

The latest EMongoCriteria: https://github.com/Sammaye/MongoYii/commit/3991c7e2d768791edcf364ab7d50ad53e4b4e090

With:




	public function search(){

		$criteria = new EMongoCriteria;

		

		if($this->_id!==null)

			$criteria->compare('_id', new MongoId($this->_id));

		//$criteria->compare('__v', $this->__v);

		return new EMongoDataProvider(get_class($this), array(

				'criteria' => $criteria,

		));

	}



Works for me.

Do let me know if it works

Great! It works!

Thank you!

Very useful extension!

Thanks :) have pushed that fix to the 3.1.1 tag

Some error again.

Document structure:


{ "_id" : ObjectId("528e58ad6740cdea44000001"), "meta" : { "created_at" : ISODate("2013-11-21T19:02:05.795Z"), "updated_at" : ISODate("2013-11-21T19:02:05.786Z") }, "profile" : { "level" : 1 }, "ids" : { "deviceid" : "3C0754607EAD_1", "secret" : "jZOzBlkamzpxi5RMJLWtX", "pushtokens" : [  {  "deviceid" : "3C0754607EAD_1",  "token" : "" } ] }, "__v" : 0 }

I try to filter by ids.deviceid: http://static.xscreenshot.com/2013/12/05/13/screen_04fbecdf53a8f997569363327cd944e3

But get the error:


MongoException

Cannot run command count(): exception: invalid regular expression operator (protected/extensions/MongoYii/EMongoCursor.php:134)

#0 protected/extensions/MongoYii/EMongoCursor.php(134): MongoCursor->count(false)

#1 protected/extensions/MongoYii/EMongoDataProvider.php(149): EMongoCursor->count()

In a debug panel I see:


User.find() 	trace 	extensions.MongoYii.EMongoDocument

Executing find: {$query:{"ids.deviceid":{"regex":"123","flags":"i"}},$project:[]}	trace 	extensions.MongoYii.EMongoDocument

What can be a reason?

What does your code look like?

You would want to do something like:

new MongoRegex('/123/i')


$this->widget('zii.widgets.grid.CGridView', array(

	'id'		=> 'user_grid',

	'dataProvider'	=> $model->search(),

	'filter'		=> $model,

	'columns'		=> array(

		array(

			'name' => 'ids.deviceid',

			'class' => 'DataColumn',

        ),

	),

));


class DataColumn extends CDataColumn {

	protected function renderFilterCellContent() {

		if(is_string($this->filter))

			echo $this->filter;

		elseif($this->filter!==false && $this->grid->filter!==null && $this->name!==null)

		{

			if(is_array($this->filter))

				echo CHtml::activeDropDownList($this->grid->filter, $this->name, $this->filter, array('id'=>false,'prompt'=>''));

			elseif($this->filter===null)

				echo CHtml::activeTextField($this->grid->filter, $this->name, array('id'=>false));

		}

		else

			parent::renderFilterCellContent();

	}


}

In a model:


public function search() {

		$criteria = new EMongoCriteria;

		$criteria->compare('ids.deviceid', $this->{"ids.deviceid"}, TRUE);

		return new EMongoDataProvider(get_class($this), array(

			'criteria' => $criteria,

		));

}



I mean what’s creating the regex query?

You must have a piece of code somewhere that creates:




Executing find: {$query:{"ids.deviceid":{"regex":"123","flags":"i"}},$project:[]}       trace   extensions.MongoYii.EMongoDocument



Yii cannot form that query on its own and compare will not partial match by default.

Edit:

Sorry I didn’t see your edit where you added your model.




$this->{"ids.deviceid"}



Cannot be valid PHP syntax unless it is something allowed in 5.4+ versions of PHP, normally that would say get $this->ids.deviceid which isn’t a valid definition of a field name so I am unsure how you are getting 123 back in the first place instead of an error.

Reference: http://stackoverflow.com/questions/1057622/dot-in-variable-name

As I remember it worked in PHP 5.3+. But I use PHP 5.5, it works here.

About the error. I just copied the “users” collection, dropped the old collection and renamed the copy to “users”. After that it became work. It’s very weird.

Thanks for trying to help.

Hi, Sammaye!

I understand how to use ESubdocumentValidator to a certain extent, but it seems that filters in rules do not work, do they?

A document in MongoDB looks like:




{

    "_id" : ObjectId("52a9bde6eb37a4c64b8b4569"),

    "ru" : {

        "title" : "Тема",

        "text" : "Текст"

    },

    "en" : {

        "title" : "Title",

        "text" : "Text"

    },

    "es" : {

        "title" : "Tema",

        "text" : "Texto"

    },

    "ar" : {

        "title" : "العنوان او نص قصير",

        "text" : "النص"

    }

}



Some of validation rules:




public function rules()

{

    return [

        ['ru,en,es,ar', 'ext.ESubdocumentValidator', 'type' => 'one', 'rules' => [

            ['title, text', 'filter', 'filter' => 'trim'] // this seems not to work


            ['title', 'length', 'max' => 500],

            ['text', 'length', 'max' => 5000],


            ['title', 'ext.YiiConditionalValidator',

                'if' => [['text', 'required']],

                'then' => [['title', 'required']],

            ],

        ]

    ];

}



I haven’t used filters in my Yii yet, how exactly do they work?

Technically it should because any value the filter returns will be thrown into the subdocument, or should anyway but the filter is most likely working on the $model variable.




class CFilterValidator extends CValidator

{

    protected function validateAttribute($object,$attribute)

    {

        ...

        $object->$attribute=call_user_func_array($this->filter,array($object->$attribute));

    }

}



A filter validator applies a specified function to the object’s attribute. It is useful, if you want to change some values before applying other validation rules. I can probably use beforeValidate(), now that I think of it, but I’m used to define it in rules.

Yeah understandable and it should be possible as you can see here: https://github.com/Sammaye/MongoYii/blob/master/validators/ESubdocumentValidator.php#L93 it takes the attributes of the model it creates and assigns them to the parent model.

But now I see the problem, it get the field value (line 82) BEFORE the validate (line 85). Should probably make a fix there to get the field value after the validation.

OK if you try the latest github checkout, not 3.1.4 but the lastest ESubdocument from master that should work.

I tried new version of ESubdocumentValidator, filters still do not work.