Symfony MongoDB ODM Left Join Query Builder - mongodb

I have two ODM document One is Item contain
class Items {
/**
* #MongoDB\Field(name="item_name", type="string")
*/
protected $itemName;
}
and another document is
class ItemLocation {
/**
* #var
* #MongoDB\ReferenceOne(targetDocument="Items")
*/
private $item;
/**
* #MongoDB\Field(name="priority", type="integer")
*/
protected $priority;
/**
* #var
* #MongoDB\ReferenceOne(targetDocument="Location")
*/
private $location;
}
How I can get all items left join with item location which is filter by location and order by priority.

In order to get all items left join with item location you can use the inversedBy and mappedBy options.
The document Items will be like this:
class Items {
/**
* #MongoDB\Field(name="item_name", type="string")
*/
protected $itemName;
/**
* #MongoDB\ReferenceMany(targetDocument=ItemLocation::class, mappedBy="item")
*/
private $items_items;
}
The document ItemLocation will be like this:
class ItemLocation
{
/**
* #var
* #MongoDB\ReferenceOne(targetDocument="Items", inversedBy="items_items")
*/
private $item;
/**
* #MongoDB\Field(name="priority", type="integer")
*/
protected $priority;
/**
* #var
* #MongoDB\ReferenceOne(targetDocument="Location")
*/
private $location;
}
In order to generate getter and setter use:
php bin/console doctrine:mongodb:generate:documents appBundle
The controller will be like this:
$dm = $this->get('doctrine_mongodb')->getManager();
$repository = $dm->getRepository('Items:Categorie');
$i = $repository->findOneBy(array('id' => 'example'));
$items = $i->getItemsItems();
Read more here.

Related

Doctrine Table Inheritance - No identifier/primary key specified for Entity

In Doctrine 2/Symfony 3 with Postgres as the database I am trying to create a table with following fields:
**actions-reviews**
id
action_id
action_task_id
document_id
review_to
review_date
review_description
review_by
is_effective
The table is either linked to a doc_id which is foreign key for an entity called Document or action_task_id for entity called ActionTask. In order to achieve this I am using inheritance mapping via the use of discriminator. In skipper the entity relationship look like this:
As you can see both the ActionTask and ActionReview are sub-set of Action entity.
My issue is when create the entities and run php bin/console doctrine:schema:update --force I get the following error:
[Doctrine\ORM\Mapping\MappingException]
No identifier/primary key specified for Entity "AppBundle\Entity\ActionTask
Review". Every Entity must have an identifier/primary key.
Reading the forums I add #ORM/Id to ActionTaskReview entity and then it updates the DB without any errors. However when I look at the postgres table only the document_id field is there not the action_task_id field. What I am doing wrong as the document_id does not throw the same error when I exclude #ORM/Id?
My Doctrine entities look like:
ActionReview.php
/**
* #ORM\Entity
* #ORM\Table(name="actions_reviews")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="owner", type="string")
* #ORM\DiscriminatorMap({"document":"AppBundle\Entity\DocumentActionReview","action_task":"AppBundle\Entity\ActionTaskReview"})
* #Discriminator(field = "owner", map = {"document": "AppBundle\Entity\DocumentActionReview", "action_task": "AppBundle\Entity\ActionTaskReview"})
*/
abstract class ActionReview
{
/**
* #ORM\Id
* #ORM\Column(type="guid")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $review_date;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $review_description;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $is_effective;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Action", inversedBy="action_review")
* #ORM\JoinColumn(name="action_id", referencedColumnName="id")
*/
private $action;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Employee")
* #ORM\JoinColumn(name="review_to", referencedColumnName="id")
*/
private $review_to;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Employee")
* #ORM\JoinColumn(name="review_by", referencedColumnName="id")
*/
private $review_by;
/**
* Get id
*
* #return guid
*/
public function getId()
{
return $this->id;
}
/**
* Set reviewDate
*
* #param \DateTime $reviewDate
*
* #return ActionReview
*/
public function setReviewDate($reviewDate)
{
$this->review_date = $reviewDate;
return $this;
}
/**
* Get reviewDate
*
* #return \DateTime
*/
public function getReviewDate()
{
return $this->review_date;
}
/**
* Set reviewDescription
*
* #param string $reviewDescription
*
* #return ActionReview
*/
public function setReviewDescription($reviewDescription)
{
$this->review_description = $reviewDescription;
return $this;
}
/**
* Get reviewDescription
*
* #return string
*/
public function getReviewDescription()
{
return $this->review_description;
}
/**
* Set isEffective
*
* #param boolean $isEffective
*
* #return ActionReview
*/
public function setIsEffective($isEffective)
{
$this->is_effective = $isEffective;
return $this;
}
/**
* Get isEffective
*
* #return boolean
*/
public function getIsEffective()
{
return $this->is_effective;
}
/**
* Set action
*
* #param \AppBundle\Entity\Action $action
*
* #return ActionReview
*/
public function setAction(\AppBundle\Entity\Action $action = null)
{
$this->action = $action;
return $this;
}
/**
* Get action
*
* #return \AppBundle\Entity\Action
*/
public function getAction()
{
return $this->action;
}
/**
* Set reviewTo
*
* #param \AppBundle\Entity\Employee $reviewTo
*
* #return ActionReview
*/
public function setReviewTo(\AppBundle\Entity\Employee $reviewTo = null)
{
$this->review_to = $reviewTo;
return $this;
}
/**
* Get reviewTo
*
* #return \AppBundle\Entity\Employee
*/
public function getReviewTo()
{
return $this->review_to;
}
/**
* Set reviewBy
*
* #param \AppBundle\Entity\Employee $reviewBy
*
* #return ActionReview
*/
public function setReviewBy(\AppBundle\Entity\Employee $reviewBy = null)
{
$this->review_by = $reviewBy;
return $this;
}
/**
* Get reviewBy
*
* #return \AppBundle\Entity\Employee
*/
public function getReviewBy()
{
return $this->review_by;
}
}
ActionTaskReview.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Exclude;
/**
* ActionTaskReview
* #ORM\Entity
*/
class ActionTaskReview
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ActionTask", inversedBy="action_review")
* #ORM\JoinColumn(name="action_task_id", referencedColumnName="id") \AppBundle\Entity\ActionTask
*/
private $action_task;
/**
* Set actionTask
*
* #param \AppBundle\Entity\ActionTask $actionTask
*
* #return ActionTaskReview
*/
public function setActionTask(\AppBundle\Entity\ActionTask $actionTask = null)
{
$this->action_task = $actionTask;
return $this;
}
/**
* Get actionTask
*
* #return \AppBundle\Entity\ActionTask
*/
public function getActionTask()
{
return $this->action_task;
}
}
DocumentActionReview.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="documents_actions_reviews")
*/
class DocumentActionReview extends \AppBundle\Entity\ActionReview
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Document", inversedBy="action_review")
* #ORM\JoinColumn(name="document_id", referencedColumnName="id")
*/
private $document;
/**
* Set document
*
* #param \AppBundle\Entity\Document $document
*
* #return DocumentActionReview
*/
public function setDocument(\AppBundle\Entity\Document $document = null)
{
$this->document = $document;
return $this;
}
/**
* Get document
*
* #return \AppBundle\Entity\Document
*/
public function getDocument()
{
return $this->document;
}
}
First of all, your ActionTaskReview should extend ActionReview:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Exclude;
/**
* ActionTaskReview
* #ORM\Entity
*/
class ActionTaskReview extends ActionReview
{
...
}
Than I am not sure what you want to achieve, as you are using Single Table Inheritance but are setting the #ORM\Table(name="documents_actions_reviews") annotation on the DocumentActionReview class. Either switch to Class Table Inheritance if you need own tables for your inherited entities or remove the annotation.
Every entity class must have an identifier/primary key. You can select the field that serves as the identifier with the #Id annotation.
`/**
* #ORM\Column(type="integer")
* #ORM\Id
* #GeneratedValue
*/
private $id;`

Symfony2, field type entity, "many-to-many" relation with extra fields, problems with the relation/ArrayCollection/multipe checkboxes in form

I want to create a form for a simple entry management.
I got the entities Entry, EntryUser, and User.
Tables in database: entry, entry_user and user.
I think i am close to be successful, but i got some problems here.
In my form, the author can check the users he want to add to the entry via checkboxes. Symfony/Doctrine should do the relation work for me and add rows into entry_user. One row for one selected checkbox.
Entry.php
/**
* #ORM\Entity
* #ORM\Table(name="entry")
*/
class Entry {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="EntryUser", mappedBy="entry", cascade={"all"}, orphanRemoval=true)
*/
protected $users;
function __construct() {
$this->users = new ArrayCollection();
}
/**
* Add user
*
* #param User $user
* #return $this
*
*/
public function addUser(User $user)
{
if (!$this->users->contains($user)) {
$this->users->add($user);
}
return $this;
}
/**
* Remove user
*
* #param User $user
* #return $this
*/
public function removeUser(User $user)
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
}
return $this;
}
/**
* #return array
*/
public function getUsers()
{
return $this->users->toArray();
}
/**
* Returns the true ArrayCollection of Users.
* #return Doctrine\Common\Collections\ArrayCollection
*/
public function getUsersCollection()
{
return $this->users;
}
}
User.php
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User implements AdvancedUserInterface, EquatableInterface, \Serializable {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
// Many other methods for user sign in, roles and so on...
}
EntryUser.php
/**
* #ORM\Entity
* #ORM\Table(name="entry_user")
*/
class EntryUser {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer", nullable=true)
*/
protected $user_id;
/**
* #var integer
*
* #ORM\Column(name="entry_id", type="integer", nullable=true)
*/
protected $entry_id;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $user;
/**
* #var Entry
*
* #ORM\ManyToOne(targetEntity="Entry", inversedBy="users")
* #ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $entry;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set user_id
*
* #param integer $userId
* #return EntryUser
*/
public function setUserId($userId)
{
$this->user_id = $userId;
return $this;
}
/**
* Get user_id
*
* #return integer
*/
public function getUserId()
{
return $this->user_id;
}
/**
* Set entry_id
*
* #param integer $entryId
* #return EntryUser
*/
public function setEntryId($entryId)
{
$this->entry_id = $entryId;
return $this;
}
/**
* Get entry_id
*
* #return integer
*/
public function getEntryId()
{
return $this->entry_id;
}
/**
* Set user
*
* #param User $user
* #return EntryUser
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return User
*/
public function getUser()
{
return $this->user;
}
/**
* Set entry
*
* #param Entry $entry
* #return EntryUser
*/
public function setEntry(Entry $entry = null)
{
$this->entry = $entry;
return $this;
}
/**
* Get entry
*
* #return Entry
*/
public function getEntry()
{
return $this->entry;
}
}
I use ManyToOne relationships here because i want to use a file named EntryUser.php to add some custom fields later. I need this because i must store some additional data there in entry_user.
Because:
And it's the right thing to do. Create a new entity with the new fields, and if you need it, create a custom repository to add the methods you need.
A <--- Many to many with field ---> B
would become
A --One to many--> C (with new fields) <-- One to many--B
and of course, C has ManyToOne relationships with both A and B.
See the comments in: ManyToMany relationship with extra fields in symfony2 orm doctrine
My EntryType.php form definition includes the following, to create the checkboxes for the template:
$builder->add('users', 'entity', array(
'class' => 'MyCompMyAppBundle:User',
'multiple' => true,
'expanded' => true,
'property' => 'name',
'label' => 'Freunde',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')->select('a')
->from('MyComp\MyAppBundle\Entity\User', 'a')
->where('EXISTS (
SELECT b
FROM MyComp\MyAppBundle\Entity\UserFriend b
WHERE b.created_by = :my_user_id
AND b.friend_user_id = a.id
)')
->andWhere('EXISTS (
SELECT c
FROM MyComp\MyAppBundle\Entity\UserFriend c
WHERE c.created_by = a.id
AND c.friend_user_id = :my_user_id
)')
->setParameter('my_user_id', $this->user->getId());
},
'required' => true,
));
As you can see, i load User objects here for the form field (type: entity).
UserFriend is another entity (table: user_friend). A friend list is saved there. Here all the friends gets loaded. They will be shows as the checkboxes.
Now, if i go to my form in my browser, and check some users for the entry und if i submit the form, i get this error:
ORMException: Found entity of type MyComp\MyAppBundle\Entity\User on association MyComp\MyAppBundle\Entity\Entry#friends, but expecting MyComp\MyAppBundle\Entity\EntryUser
So it is very confusing.
How can i make this work?
How can i make symfony and doctrine work to insert data automatically into entry and entry_user?
Important: I want to make it possible that the author can edit the entry later. So the checkboxes should be selected by default as it is saved in entry_user.
I just try to understand this and got a little bit confused. But i dont know how to make this work.
I found an awesome solution in the web. I was searching for many days now, but this guy made my day! :-)
You can read how it works and learn from an awesome example here!
Happy coding!
For this case you should check "form collections" in Symfony: http://symfony.com/doc/current/cookbook/form/form_collections.html
With this technique you will add a form type to crate a single "EntryUser" and after that you can add a collection of that form to the parent form. Quite easy and well explained in the liked article.

Doctrine ODM clone document with referenceMany

I have a problem when i want persist my clone document.
The clone is ok but when i persist a document i lose the referenceMany. Why?
All variable are persist except the variable with #MongoDB\ReferenceMany is skip.
/**
* #MongoDB\Document
*/
class Notice{
/**
* #MongoDB\String
*/
protected $idPatern = '';
/**
* #MongoDB\ReferenceOne(
* targetDocument="Wikiludo\NoticeBundle\Document\NombreJoueur",
* cascade="all"
* )
*/
protected $nombreJoueur;
/**
* #MongoDB\ReferenceMany(
* targetDocument="Wikiludo\NoticeBundle\Document\NoteSurPublic",
* cascade="all"
* )
*/
protected $noteSurPublics;
public function __clone() {
if ($this->id) {
$this->setId(null);
}
}
[...]
}

Editing Slug using Gedmo TreeSlugHandler

I have an entity that is hierarchical using the Gedmo Tree Doctrine extension in Symfony 2. The code for the Category entity is:
<?php
namespace MD\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use MD\Entity\Extension\Treeable;
/**
* Category
*
* #ORM\Entity(repositoryClass="MD\Entity\Repository\CategoryRepository")
*
* #Gedmo\Tree(type="nested")
*/
class Category
{
/**
* Entity Extensions
*/
use Treeable;
/**
* The ID of the category
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* The title of the category
*
* #var string
*
* #ORM\Column(type="string", length=255)
*
* #Assert\NotBlank(message="category.title.not_blank")
* #Assert\Length(
* max=255,
* maxMessage="category.title.length.max"
* )
*/
protected $title;
/**
* The description of the category
*
* #var string
*
* #ORM\Column(type="text")
*
* #Assert\NotBlank(message="category.description.not_blank")
*/
protected $description;
/**
* The parent of the category
*
* #var Category
*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*
* #Gedmo\TreeParent
*/
protected $parent;
/**
* The children of the category
*
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent", cascade={"persist"})
* #ORM\OrderBy({"left" = "ASC"})
*/
protected $children;
/**
* The slug of the category
*
* #var string
*
* #ORM\Column(type="string", length=255, unique=true)
*
* #Gedmo\Slug(handlers={
* #Gedmo\SlugHandler(class="Gedmo\Sluggable\Handler\TreeSlugHandler", options={
* #Gedmo\SlugHandlerOption(name="parentRelationField", value="parent"),
* #Gedmo\SlugHandlerOption(name="separator", value="/")
* })
* }, fields={"title"})
*/
protected $slug;
/**
* Constructor
*/
public function __construct()
{
$this->children = new ArrayCollection();
}
/**
* Get the ID of the category
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set the title of the category
*
* #param string $title
*
* #return $this
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get the title of the category
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set the description of the category
*
* #param string $description
*
* #return $this
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get the description of the category
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set the parent of the category
*
* #param Category $parent
*
* #return $this
*/
public function setParent(Category $parent = null)
{
$this->parent = $parent;
if (null !== $parent) {
$parent->addChild($this);
}
return $this;
}
/**
* Get the parent of the category
*
* #return Category
*/
public function getParent()
{
return $this->parent;
}
/**
* Add a child to the category
*
* #param Category $child
*
* #return $this
*/
public function addChild(Category $child = null)
{
if (!$this->children->contains($child)) {
$this->children->add($child);
$child->setParent($this);
}
return $this;
}
/**
* Get the children of the category
*
* #return ArrayCollection
*/
public function getChildren()
{
return $this->children;
}
/**
* Set the slug of the category
*
* #param string $slug
*
* #return $this
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get the slug of the category
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/** Magic Methods */
/**
* Return a string representation of the category
*
* #return string
*/
public function __toString()
{
return $this->getTitle();
}
}
Given a category with a title of Bands and a sub-category with a title of Rock, the latter category, on creation, has a slug of bands/rock. This works as expected.
When I add the slug field to a form, however, when I edit the entity, I initially get bands/rock added to the form field. If I change this to bands/rock-and-roll and submit the form, the slug gets updated to bands/bands-rock-and-roll and not bands/rock-and-roll as I'd expect.
If I edit the category and set the slug field to rock-and-roll, then submit the form, the slug gets updated to bands/rock-and-roll. I'd expect the slug to be rock-and-roll after update.
What do I need to do to fix this behaviour? I essentially want the slug to be auto-generated with the handler if it's not provided, but to be set to exactly what I provide if I do provide it.
Thanks
looking at the docs of the Gedmo Tree Doctrine extension and it's relative code, it's not a wrong behaviour because the slug is composed accordingly with the "TreeSlugHandler" method = parentFieldName/field-You-Have-Inserted (that happens every time you edit the slug).
If you need at the same moment of a slug for a specific category and of another that follows the category tree you can add another property (ex: cat_slug) with the simple annotation: #Gedmo\Slug(fields={"fieldYouWantToSlug"}).
Remember that every time (using the "TreeSlugHandler" method) you edit the slug (changing the precedent), every subcategory will be updated with the new slug.
I hope I was helpful

Zend - doctrine 2 querybuilder help

i want to use this:
$query = $this->_doctrine->createQueryBuilder()
->select('u')
->from('\Entities\Users', 'l')
->leftJoin('l.userentities', 'u')
->getQuery();
return $info = $query->getResult();
and my users entity is:
namespace Entities\Users;
/**
* #Entity
* #Table(name="users")
* #HasLifecycleCallbacks
*/
class Users extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="userid", type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $userid;
/** #Column(name="itemid", type="integer") */
protected $itemid;
}
and my user entities entity class contains:
namespace Entities\Users;
/**
* #Entity
* #Table(name="userentities")
* #HasLifecycleCallbacks
*/
class Userentities extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="entityid", type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $entityid;
/** #Column(name="userid", type="integer") */
protected $userid;
/** #Column(name="crb", type="string") */
protected $crb;
}
i tried put this into the entity but no joy
* #OneToMany(targetEntity="Users", inversedBy="userid")
* #JoinColumn(name="userid", referencedColumnName="userid")
1 user has many user entities..
and i get this error:
Error: Class Entities\Users has no association named Users
i just want to do a left join with users to usersentities..
how can i doa left join?
Try putting this annotation in your Userentities class for the $userid field:
/**
* #var Entities\Userentities
* #ManyToOne(targetEntity="Users", inversedBy="userentities", cascade={"persist"})
*/
private $userid;
and this annotation in your Users class with a new field called $userentities:
/**
* #var \Doctrine\Common\Collections\ArrayCollection
* #OneToMany(targetEntity="Userentities", mappedBy="userid", cascade={"persist", "remove"})
*/
private $userentities;