MongoDB ODM - Group by MAX(group_id) - mongodb

Let me get to the point, I am currently using the Doctrine MongoDB ODM in conjunction with Symfony2 to persist data into MongoDB.
Currently I am grouping my results by type, but I would like to group them by MAX(group_id) as well.
Sure I can just alter the reduce function, but I am trying to steer clear of a large return array and more processing once the query is done, so I was wondering if there is a more elegant solution than that to this particular problem.
The Monitoring document,
/**
* #ODM\Document(collection="monitoring")
*/
class Monitoring
{
/** #ODM\Id */
public $id;
/** #ODM\String */
public $type;
/** #ODM\String */
public $message;
/** #ODM\Int */
public $groupId;
.... getters and setter etc ....
}
MonitoringManager function to fetch all items,
public function getAllMonitoringItems(){
return $this->dm->createQueryBuilder('MonitoringBundle:Monitoring')
->group(array(), array('groups' => array()))
->reduce('function (obj, prev) {
var type = obj.type;
if(!prev.groups.hasOwnProperty(type)){
prev["groups"][type] = [];
prev["groups"][type].push(obj);
} else {
prev["groups"][type].push(obj);
}
}')
->field('type')->notIn(array("graph"))
->getQuery()
->execute()
->toArray();
}

Related

Laravel relationship with a pivot table

I have 3 tables:
Category ( id, name)
Category_Tournament (category_id, tournament_id) --> pivot table
Category_Tournament_User (category_id, tournament_id, user_id, confirmed)
Category is the list of available categories
Category_Tournament is the list of categories the admin configured
Category_tournament_User is the categories the user has registred
To get all categories in the tournament, I can do it easily with:
tournament->categories
defining a belongsToMany relationship in tournament model
What I don't know how to define relationship with the last table.
What I need is the user click on several categories, and I can run something like:
tournament->user_categories->sync($userCategories)
where I should sync table Category_Tournament_User ( with category_id, tournament_id, user_id)
What is the best way to achieve it???
EDIT:
Model Tournament:
class Tournament extends Model
{
protected $table = 'tournament';
public $timestamps = true;
protected $fillable = [
'name',
'date',
'type',
];
/**
* A tournament is owned by a user
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
{
return $this->belongsTo('App\User', 'user_id','id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function categories()
{
return $this->belongsToMany('App\Category')
->withTimestamps();
}
}
Model Category
class Category extends Model
{
protected $table = 'category';
public $timestamps = true;
protected $fillable = [
'id',
'name',
];
public function tournaments()
{
return $this->belongsToMany('App\Tournament');
}
}
Model User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, HasRole;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name','firstname','lastname','email', 'password','avatar',country_id','role_id',,'provider','provider_id','verified'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function ($user) {
$user->token = str_random(30);
});
}
public function role()
{
return $this->belongsTo('App\Role');
}
public function settings()
{
return $this->hasOne('App\Settings');
}
public function invites()
{
return $this->hasMany('App\Invite', 'email','email');
}
public function country()
{
return $this->belongsTo('Webpatser\Countries\Countries');
}
/**
* A user can have many tournaments
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tournaments()
{
return $this->hasMany('App\Tournament');
}
}
You have many to many relationship here between User and Category_Tournament and you should take a look in documentation exactly at Many To Many.
I think you don't need to to have Category_Tournament_User table. and you can't make a Model for it in Laravel. you only need to a table user_tournament. and you should define relation(foreign key) on migration, like this:
Schema::create('user_tournament', function(Blueprint $table){
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('tournament_id')->unsigned();
$table->unique(['tournament_id', 'user_id']);//You can omit this
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('tournament_id')->references('id')->on('tournaments')->onDelete('cascade')->onUpdate('cascade');
$table->nullableTimestamps();
});
then you can use this code:
user->tournaments->sync($userCategories);

How to filter entities displayed in Symfony2 form collection

Is there a way to filter the entities collected by Symfony2 in a form collection?
My scenario is this;
2 entities: parent and child.
The child entity has a property 'birthdate'.
There is a manyToMany relationship between the 2 tables.
I have a formType (parentType) that contains a collection of childType forms.
I can load parentType and it loads every childType that is associated to the parent record.
I want to filter the childType collection so that records with a birthdate greater than a date are included and those less than the date are excluded.
The Symfony2 collection form type does not allow the use of 'query_builder' to filter the selection in builder->add().
Has anyone faced or solved this problem?
My solution is to use separate setter/getter for child entity collection and filter getter output with Criteria. Form field name should be "filteredChilds". A bit hacky but should do the trick.
Entity/Parent.php
<?php
...
use Doctrine\Common\Collections\Criteria;
...
class Parent
{
...
/**
* #param Child $child
* #return $this
*/
public function addChild(Child $child)
{
$this->childs[] = $child;
return $this;
}
/**
* #param Child $child
*/
public function removeChild(Child $child)
{
$this->childs->removeElement($child);
}
/**
* #return ArrayCollection
*/
public function getChilds()
{
return $this->childs;
}
/**
* #param Child $child
* #return $this
*/
public function addFilteredChild(Child $child)
{
$this->addChild($child);
}
/**
* #param Child $child
*/
public function removeFilteredChild(Child $child)
{
$this->removeChild($child);
}
/**
* #return ArrayCollection
*/
public function getFilteredChilds()
{
$criteria = Criteria::create()
->where(Criteria::expr()->gt("birthday", new \DateTime()));
return $this->getChilds()->matching($criteria);
}
...
}

Doctrine MongoDB - Query on Document with multiple level of embedded document

I have some problems with embedding document with Doctrine MongoDB ODM and Symfony 2.
To expose the problem, I have the document product embedOne productInformation, and productInformation embedOne productInformationAddress.
To query, I use something like that :
/**
* #ODM\Document
**/
class product {
/**
* #ODM\EmbedOne(targetDocument="productInformation")
**/
protected $informations;
}
/**
* #ODM\EmbeddedDocument
**/
class productInformations {
/**
* #ODM\EmbedOne(targetDocument="productInformationAddress")
**/
protected $address;
/**
* #ODM\Collection
**/
protected $attr1 = array();
/**
* #ODM\String
**/
protected $attr2
}
/**
* #ODM\EmbeddedDocument
**/
class productInformationAddress {
/** ... suff ... /*
}
When I query :
class productRepository {
public function fetchOne($id) {
return $this->createQueryBuilder()
->field('id')->equals($id)
->getQuery()
->getSingleResult();
}
}
But, I don't understand why I cannot get $product->getInformations()->getAddress(), that always return null...
Any idea?
I don't see a problem with the code you posted, but it would probably be helpful to read through ODM's functional tests for nested, embedded documents. In EmbeddedTest.php, the methods of interest would be anything that uses EmbeddedTestLevel2, which is equivalent to your productInformationAddress class, and testRemoveAddDeepEmbedded().

Symfony2 && mongodb simple reference

he guys,
i want to make a simple reference on mongodb documents using symfony2.
i have this two documents and want to store picture references into the requests document. it works for me, if i have only the picture ids in the requests document.
so i need the follow:
can everyone change the document files and make and extends the custum call to get all pictures as object from the requests (picture array)?
my original files:
Document Pictures:
<?php
namespace TestBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(repositoryClass="TestBundle\Repository\RequestsRepository")
*/
class Requests
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
*/
protected $title;
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}
Document Pictures:
<?php
namespace TestBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(repositoryClass="TestBundle\Repository\PicturesRepository")
*/
class Pictures
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
*/
protected $filename;
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setFilename($filename)
{
$this->filename = $filename;
}
public function getTitle()
{
return $this->filename;
}
}
My Basic Calls:
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$request = $dm->getRepository('TestBundle:Requests')->find($requestId);
To my tests:
i added in the requests document the follow:
/**
* #MongoDB\ReferenceMany(targetDocument="Pictures",cascade={"persist"},simple="true")
*/
protected $pictures = array();
public function setPictures($pictures)
{
$this->pictures[] = $pictures;
}
public function getPictures()
{
return $this->pictures;
}
and added pictures like this:
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$photo = $dm->getRepository('TestBundle:Pictures')->find($photoId);
$dm1 = $this->get('doctrine.odm.mongodb.document_manager');
$request = $dm1->getRepository('TestBundle:Requests')->find($requestId);
$request->setPictures($photo);
$dm1->flush();
this works - but i cannot get the pictures by loading the document.
my code to load:
$dm1 = $this->get('doctrine.odm.mongodb.document_manager');
$request = $dm1->getRepository('TestBundle:Requests')->find($requestId);
$pictures = $request->getPictures();
foreach($pictures as $picture)
{
print $picture->getId();
}
THIS WILL NOT WORK. i become the follow error:
Fatal error: Doctrine\ODM\MongoDB\Proxy\ProxyFactory::getProxy():
Failed opening required
'.../app/cache/dev/doctrine/odm/mongodb/Proxies/_CG_TestBundleDocumentPictures.php'
(include_path='.:.../library:/usr/local/zend/share/pear') in
..../test/vendor/doctrine-mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php
on line 100
thanks, jan
First off you only need to call doctrine one time in $dm your overloading your resources and thats bad practice. One function, one Doctrine call. Secondly, you need a $dm->persist($request) and then $dm->flush(). Create a OnetoOne between your Documents and then make $pictures an Doctrine Array Collection. Then set a picture like you tried, then make a smiple query and call $request->getPicture()->getId().
Ok i found the error:
In the deps file i have the following lines:
[doctrine-common]
git=http://github.com/doctrine/common.git
version=2.1.4
[doctrine-dbal]
git=http://github.com/doctrine/dbal.git
version=2.1.7
[doctrine]
git=http://github.com/doctrine/doctrine2.git
version=2.1.7
After updating them to:
[doctrine-common]
git=http://github.com/doctrine/common.git
version=2.2.1
[doctrine-dbal]
git=http://github.com/doctrine/dbal.git
version=2.2.1
[doctrine]
git=http://github.com/doctrine/doctrine2.git
version=2.2.1
And doing php bin/vendors update the references will work again

Joining-Table with Metadata Impairs Getters/Setters - Doctrine 2

I'm writing a feature which calls for the records of my joining table to carry extra metadata (Joining-Table with Metadata). I've attempted to implement this in accordance with this section of the Doctrine documentation.
See below for example Entity definitions.
The challenge now is that getGroups and setGroups do not yield/set Group entities (& the same is true from the Group instance perspective), but they yield GroupUser entities.
This adds a substantial delay to process of managing this relationships, which so far have been extremely smooth - for example, I cannot simply add, remove, or check for the existence of a Group to the collection which getGroups yields.
Can anyone identity any errors in my implementation, or else recommend a more fluid way of implementing this concept?
Thanks in advance for any input.
EDIT:
My main concern is this: using this implementation, retrieving a collection of Users from a Group entity requires this Entity method's mediation:
public function getUsers() {
return $this->users->map(function($groupUser){
return $groupUser->getUser();
});
}
I'm concerned that this could imply a major performance hit down the road. Am I incorrect?
Furthermore, how does one re-implement the setUsers method?
Group entity:
<?php
/**
* #Entity
* #Table(name="group")
*/
class Group {
/**
* #Column(type="integer", nullable=false)
* #Id
*/
protected $id = null;
/**
* #OneToMany(targetEntity="GroupUser", mappedBy="group")
* #var \Doctrine\Common\Collections\Collection
*/
protected $users;
}
User entity:
<?php
/**
* #Entity
* #Table(name="user")
*/
class User {
/**
* #Column(type="integer", nullable=false)
* #Id
*/
protected $id = null;
/**
* #OneToMany(targetEntity="GroupUser", mappedBy="user")
* #var \Doctrine\Common\Collections\Collection
*/
protected $groups;
}
Joining entity:
<?php
/**
* #Entity
* #Table(name="group_user")
*/
class GroupUser {
/**
* #Id
* #ManyToOne(targetEntity="User", inversedBy="groups")
* #JoinColumn(name="userId", referencedColumnName="id")
*/
protected $user;
/**
* #Id
* #ManyToOne(targetEntity="Group", inversedBy="users")
* #JoinColumn(name="groupId", referencedColumnName="id")
*/
protected $group;
/**
* #Column(type="integer")
*/
protected $relationship;
}
Related -
Same goal, slightly different approach, which consistently produced errors once the resulting collections were manipulated: http://www.doctrine-project.org/jira/browse/DDC-1323
Supports the approach, no technical details: Doctrine 2 join table + extra fields
I've found just two examples (see question) of entity definitions for this specific type of relationship, however no example code for how they're used. As such it was fairly unclear how fluid (or otherwise) the resulting setters & getters could be expected to be. Hopefully this code will help clear up the approach for anyone else making a similar attempt.
The ideal solution under the circumstances (thanks #doctrine # freenode) was to implement a custom repository - a more flexible & efficient place for creating & managing the association.
Example Custom Repository for Join-Table with Metadata Class - Solution accompanies code in original question
<?php
use Doctrine\ORM\EntityRepository;
class GroupUserRepository extends EntityRepository {
/**
* #param \User $user
* #param \Group $group
* #param integer $type One of the integer class constants defined by GroupUser
* #param string $role Optional string defining user's role in the group.
* #return \GroupUser
*/
public function addUserToGroup(User $user, Group $group, $relationship, $role = '') {
$groupUser = $this->findOneBy(array('user' => $user->getId(), 'group' => $group->getId()));
if(!$groupUser) {
$groupUser = new GroupUser();
$groupUser->setGroup($group);
$groupUser->setUser($user);
$groupUser->setRole($role);
$groupUser->setRelationship($relationship);
$this->_em->persist($groupUser);
}
return $groupUser;
}
/**
* #param \User $user
* #param \Group $group
* #return null
*/
public function removeUserFromGroup(User $user, Group $group) {
$groupUser = $this->findOneBy(array('user' => $user->getId(), 'group' => $group->getId()));
if($groupUser)
$this->_em->remove($groupUser);
}
}
Then, from the join-table class, modify the Entity meta-data accordingly to specify the custom repository.
<?php
/**
* #Entity(repositoryClass="\Path\To\GroupUserRepository")
*/
class GroupUser {
// ...
}
This causes the custom repository to yield in place of the default one, making a proxy method from the Entity class simple.
<?php
/**
* #Entity
*/
class Group {
/**
* #param \User $user
* #param integer $relationship One of the integer class constants defined by GroupUser
* #param string $role Optional string defining user's role in the group.
* #return \GroupUser
*/
public function addUser(User $user, $relationship, $role = '') {
return $this->_em->getRepository('GroupUser')
->addUserToGroup($user, $this, $relationship, $role);
}
}
And things are about as manageable as they were before.