Doctrine: Get one object of my collection (relation ManyToMany bidireccional) - php

I need to get a specific object from my collection Doctrine.
Currently, I have two entity (with ManyToMany bidirectional relationship):
Categoy
User
And in my User entity, I have a board property defined as an ArrayCollection:
/**
* #ORM\ManyToMany(targetEntity="\MyNamespace\WebsiteBundle\Entity\Category", inversedBy="users", cascade={"remove"})
* #ORM\JoinTable(name="user_categories")
*/
private $categories;
public function __construct()
{
parent::__construct();
$this->categories = new ArrayCollection();
}
/**
* get Categories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
And in my Categories Entity, I have this:
/**
* #ORM\ManyToMany(targetEntity="\MyNamespace\UserBundle\Entity\User", mappedBy="categories")
* #Exclude
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* get Users
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
When I need to get all the categories in my database I process like this:
$user = $this->getUser();
$categories = $user->getCategories()
\Doctrine\Common\Util\Debug::dump($categories) // OK: Result is the categories belonging to the user
But now I want to retrieve only the category of the user with the category name "Sport".
How do I do this? I have to use the QueryBuilder and does not pass directly through my object ?
Finaly, I just want to add a condition to my $user->getBoards()
Thank's for your help !

I will definitely go for the query builder, but i think you can achieve wat you want with filters, it will be kind of tricky but here is the doc hope it helps http://doctrine-orm.readthedocs.org/en/latest/reference/filters.html

Finaly, I have create a method in my categoryRepositor, like this:
public function findOneUserCategoryById($user_id, $board_id)
{
$query = $this->getEntityManager()
->createQuery('
SELECT c FROM WebsiteBundle:Category c
JOIN c.users u
WHERE u.id = :user_id AND c.id = :category_id'
)
->setParameters(array(
'user_id' => $user_id,
'category_id' => $category_id)
);
try {
return $query->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
That work's fine and I use this method like this:
$em = $this->getDoctrine();
$category = $em->getRepository("WebsiteBundle:Category")
->findOneUserCategoryById(1, 5);

Related

join tables in doctrine2

I am trying join the tables, in Doctrine2 Query Builder. This was my query to join the two tables and get the result.
Initially i have two tables to be joined:
--> employees
--> org_employees
Other tables:
--> users
Here in my Doctrine2 Query builder code, i have joined 2 tables to fetch the results of particular Organization employees by passing the Organization id.
SELECT
*
FROM org_branch_employees oe
LEFT JOIN employees e ON oe.employee_id = e.id
WHERE
oe.org_id = 1;
By using the Query Builder the above sql code has been changed like below.
$qb = $this->entityManager->createQueryBuilder();
$qb->select('oe', 'e', 'o')
->from('Employee\Entity\OrgEmployee', 'oe')
->leftJoin('oe.organization', 'o')
->leftJoin('oe.employee', 'e')
->where('oe.organization = :organizationId')
->setParameter('organizationId', $orgId);
$query = $qb->getQuery();
$orgEmployees = $query->getResult();
return $orgEmployees;
This is my Employee Entity:
<?php
namespace Employee\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
use Users\Entity\User;
use Organization\Entity\Organization;
//use Organization\Entity\OrgEmployee;
/**
* Description of Employee
*
* #author Macwin
*/
/**
* #ORM\Entity
* #ORM\Table(name="employees")
*/
class Employee extends BaseEntity {
/**
* #ORM\OneToOne(
* targetEntity="Users\Entity\User"
* )
* #ORM\JoinColumn(
* name="user_id",
* referencedColumnName="id",
* nullable=false
* )
*/
private $user;
/**
* #ORM\Column(name="employee_code", type="string", nullable=true)
* #var string
*/
protected $empCode;
/**
* #ORM\OneToOne(
* targetEntity="Organization\Entity\Organization"
* )
* #ORM\JoinColumn(
* name="org_id",
* referencedColumnName="id",
* nullable=false
* )
*/
private $organization;
/**
* #ORM\OneToMany(targetEntity="Employee\Entity\OrgEmployee", mappedBy="employee")
*/
protected $orgEmployee;
/**
* #ORM\OneToMany(targetEntity="Employee\Entity\OrgEmployeeDesignation", mappedBy="employee")
*/
protected $orgEmployeeDesignation;
public function __construct() {
$this->organizations = new \Doctrine\Common\Collections\ArrayCollection();
parent::__construct();
}
public function getOrganizations() {
return $this->organizations;
}
public function addOrganization(Organization $organization = null) {
$this->organizations->add($organization);
}
public function setUser(User $user = null) {
$this->user = $user;
}
public function getUser() {
return $this->user;
}
public function getEmpCode() {
return $this->empCode;
}
public function setEmpCode($empCode) {
$this->empCode = $empCode;
}
public function setOrganization(Organization $organization = null) {
$this->organization = $organization;
}
public function getOrganization() {
return $this->organization;
}
function getOrgEmployeeDesignation() {
return $this->orgEmployeeDesignation;
}
function setOrgEmployeeDesignation($orgEmployeeDesignation) {
$this->orgEmployeeDesignation = $orgEmployeeDesignation;
}
public function getOrgEmployee() {
return $this->orgEmployee;
}
public function __toString() {
return __CLASS__ . ": [id: {$this->id}, name: {$this->name}]";
}
}
Here is my OrgEmployee Entity Which maps the Organization table and Enity table, to get the Organization details and Employee Details.
<?php
namespace Employee\Entity;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
use Employee\Entity\Employee;
use Organization\Entity\Organization;
/**
* Description of Org Employees
*
* #author Macwin
*/
/**
* #ORM\Entity
* #ORM\Table(name="org_branch_employees")
*/
class OrgEmployee extends BaseEntity{
/**
* #ORM\ManyToOne(targetEntity="Employee\Entity\Employee", inversedBy="orgEmployee")
* #ORM\JoinColumn(name="employee_id",referencedColumnName="id",nullable=false)
*/
protected $employee;
/**
* #ORM\ManyToOne(targetEntity="Organization\Entity\Organization", inversedBy="orgEmployee")
* #ORM\JoinColumn(name="org_branch_id", referencedColumnName="id", nullable=false)
*/
protected $organization;
public function setEmployee(Employee $employee = null)
{
$this->employee = $employee;
return $this;
}
public function getEmployee()
{
return $this->employee;
}
public function setOrganization(Organization $organization = null)
{
$this->organization = $organization;
return $this;
}
public function getOrganization()
{
return $this->organization;
}
}
Here is how i am getting the Organization details and Employee Details:
'employeeCode' => $orgEmp->getEmployee()->getEmpCode(),
userFirstName = $orgEmp->getEmployee()->getUser()->getFirstName(),
Being, employees table has mapping of users table, i can fetch the users information,
so far so good, but while i am trying to join more tables here, i couldnt bring the exact result.
But when i need to make the filter in the above functionality, i am not sure, how can bring the exact result.
Like filter by employee_code, user first name.
Can anyone guide me to bring the result. I am working on the REST API side to give the result to the client. Pagination was also there in the functionality.
I am trying the following if i am rightly said:
SELECT
*
FROM org_branch_employees oe
LEFT JOIN employees e ON oe.employee_id = e.id
LEFT JOIN users u ON e.user_id = u.id
WHERE
oe.org_id = 1 AND
u.user_first_name = "John" and
e.employee_code = "EMP777"
So the first query is working as needed? You just need to join the user entity and add some where conditions? Or maybe I misunderstood the problem.
$qb = $this->entityManager->createQueryBuilder();
$qb->select('oe', 'e', 'o', 'user')
->from('Employee\Entity\OrgEmployee', 'oe')
->leftJoin('oe.organization', 'o')
->leftJoin('oe.employee', 'e')
->leftJoin('e.user','user')
->andWhere('oe.organization = :organizationId')
->setParameter('organizationId', $orgId),
->andWhere('user.user_first_name = :userFirstName')
->setParameter('userFirstName', 'John'),
->andWhere('e.employee_code = :employeeCode')
->setParameter('employeeCode', 'Emp666');
return $qb->getQuery()->getResult();

How to remove a single record from many to many relationship in symfony usiing doctrine?

here are the the two classes with the functions involved
section class has many to many relation with student class
class Section
{
/**
* #ORM\ManyTOMany(targetEntity="Student",inversedBy="sections")
*/
private $students;
public function __construct() {
$this->students = new ArrayCollection();
}
/**
* Add students
*
* #param \Blogger\sectionBundle\Entity\Student $students
* #return Section
*/
public function addStudent(\Blogger\sectionBundle\Entity\Student $students)
{
$this->students[] = $students;
return $this;
}
/**
* Remove students
*
* #param \Blogger\sectionBundle\Entity\Student $students
*/
public function removeStudent(\Blogger\sectionBundle\Entity\Student $students)
{
$this->students->removeElement($students);
}
/**
* Get students
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getStudents()
{
return $this->students;
}
}
and
class Student {
/**
* #ORM\ManyToMany(targetEntity="Section", mappedBy="students")
*/
private $sections;
/**
* #ORM\Column(type="string")
*/
protected $studentId;
public function __construct() {
$this->sections = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add sections
*
* #param \Blogger\sectionBundle\Entity\Section $sections
* #return Student
*/
public function addSection(\Blogger\sectionBundle\Entity\Section $sections)
{
$this->sections[] = $sections;
return $this;
}
/**
* Remove sections
*
* #param \Blogger\sectionBundle\Entity\Section $sections
*/
public function removeSection(\Blogger\sectionBundle\Entity\Section $sections)
{
$this->sections->removeElement($sections);
}
/**
* Get sections
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSections()
{
return $this->sections;
}
}
as in mysql
DELETE from student_section
where student_id = (select student.id from student where student.name="dummy")
And section_id = 1
whats wrong with:
public function removeStudent(Student $student)
{
$this->students->removeElement($student);
}
You can use the generic doctrine command to generate getters and setters.
app/console doctrine:generate:entities NameSpace:Entity
Also you should read about synchronizing ManyToMany bidirectional relationships with Doctrine. Here you can read about the "adders" but the same logic applies to remove methods.
EDIT - After question was updated
If I understood you correctly you want to remove a student from a section when you have the student name. The created student_section is a generated table from Doctrine. You can execute normal PDO statements in your Controllers or Repositories, but I would personaly implement a function in the model to keep it as OOP as possible.
public function removeStudentByName(Student $student)
{
$toRemove = $this->students->filter(function (Student $s) use ($student) {
return ($->getName() == $student->getname());
});
foreach ($toRemove as $student) {
$this->students->remove($student);
}
}
In a controller you can do something like:
//$student, $em is fetched
$section->removeStudentByName($student);
$em->flush();
sorry for my misleading and unclear Question
i found what i was searching for
//in the controller:
$section = $em->getRepository('BloggersectionBundle:Section')->find(2);
$student = $em->getRepository('BloggersectionBundle:Student')->findByStudentId("555555");
$student->removeSections($section);
$em->flush();
and in Student model
public function removeSections(Section $sections)
{
$sections->removeStudent($this);
$this->sections->removeElement($sections);
}
and finally i edited the anotation in both student and section
to cascade remove
* #ORM\ManyToMany(targetEntity="Section", mappedBy="students", cascade={"persist", "remove"})

Doctrine 2 - How to get Categories without specific Article

Iam learning Doctrine.
I have two entities Article and Category in many to many relationship and iam trying to get all categories where isnt specific article.
ArticleEntity:
class Article extends BaseEntity
{
use Identifier;
/**
* #ORM\Column(type="string", nullable = false, unique=TRUE)
* #var string
*/
private $title;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="articles")
* #ORM\JoinTable(name="article_categories")
*/
private $categories;
public function getCategories()
{
return $this->categories;
}
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
And CategoryEntity:
class Category extends BaseEntity
{
use Identifier;
/**
* #ORM\Column(type="string", nullable = false, unique=true)
* #var string
*/
private $title;
/**
* #ORM\Column(type="string",nullable=false,unique=true)
* #var sting
*/
private $slug;
/**
* #ORM\ManyToMany(targetEntity="Article", mappedBy="categories")
*/
private $articles;
public function __construct()
{
$this->articles = new \Doctrine\Common\Collections\ArrayCollection();
}
And i am trying to get all categories without specific article. In pure MySQL id would be something like this:
SELECT * FROM category LEFT JOIN article_categories ON category.id = article_categories.category_id WHERE article_categories.article_id <> 1(for example) AND article_id IS NOT NULL
And the only solution i could create in my CategoryRepository is this one.
public function findWithoutArticle($article_id)
{
$articleCat = $this->em->find(Article::getClassName(), $article_id);
$qb = $this->em->createQueryBuilder();
$qb->select('c')
->from(Category::getClassName(), 'c')
->where('c.id NOT IN (:article_id)')
->setParameter('article_id', $articleCat->getCategories()->toArray());
return $qb->getQuery()->getResult();
}
And this doesnt look right. Is there any better "Doctrine way" practice?
How about this one?
$qb = $this->em->createQueryBuilder();
$qb->select('c');
$qb->from('Category', 'c');
$qb->leftJoin('c.articles', 'a');
$qb->where($qb->expr()->neq('a.article_id', '?1'));
$qb->setParameter(1, $article_id);
$categories = $qb->getQuery()->getResult();
How about this:
public function findWithoutArticle($article_id)
{
$qb = $this->em->createQueryBuilder()
->select('c')
->from('Category', 'c')
->leftJoin('c.article', 'a')
->where('a.article_id <> :articleId')
->setParameter('articleId', $article_id);
return $qb->getQuery()->getResult();
}

doctrine2 insert arraycollection in database

I am trying to insert an array collection in my database. The relation between the objects is ManyToMany . So i want to post a message and add some hashtags (not just one, a few for example stored in a Doctrine 2 array collection). There is no error, but the objects are not linked: (The tables messages and hastags both contain data, but the messages_hastags table is empty.
My code:
Message.php
/**
* #ORM\ManyToMany(targetEntity="Application\Entity\Hashtag", mappedBy="messages")
*/
private $hashtags;
public function __construct()
{
$this->hashtags = new ArrayCollection();
}
function getHashtags() {
return $this->hashtags;
}
function setHashtags($hashtags) {
$this->hashtags = $hashtags;
}
Hashtag.php
public function __construct()
{
$this->messages = new ArrayCollection();
}
/** #ORM\ManyToMany(targetEntity="Application\Entity\Message", inversedBy="hashtags") */
private $messages;
function getMessages() {
return $this->messages;
}
function setMessages($messages) {
$this->messages = $messages;
}
Controller.php
$hashtag_array = new \Doctrine\Common\Collections\ArrayCollection();
$hashtag_array->add(HASHTAG); //here is a for loop adding some entities
$newMessage = \Application\Entity\Message();
$newMessage->setHashtags($hashtag_array);
$em->persist($newMessage);
$em->flush();
The message will appear in the database but without the link to the hashtags.
Your mapping is seriously wrong.
Both the inversedBy and mappedBy fields are pointing to "hashtags". And one of them has even a typo (hastags).
In you message it should be mappedBy="messages".
You also need to always initialize your collections in the constructor!
So inside the Hashtag entity:
public function __construct()
{
$this->messages = new ArrayCollection();
}
I would suggest to first fix all this and then check if your issues are solved.
UPDATE
You cannot do:
$newMessage->setHashtags($hashtag_array);
Doctrine collections cannot be directly exchanged with an array like this.
You have to add proper setter and getter methods as written in the Doctrine 2 documentation chapter 8. Working with Associations. I would suggest doing some documentation reading before you continue working with Doctrine. To make these things work it is important to understand the Doctrine internals.
This is what it should look like inside your Message resource:
/**
* Get hashtags
*
* #return Collection
*/
public function getHashtags()
{
return $this->hashtags;
}
/**
* Add hashtag.
*
* #param Hashtag $hashtag
* #return self
*/
public function addHashtag(Hashtag $hashtag)
{
$this->hashtags->add($hashtag);
return $this;
}
/**
* Add hashtags.
*
* #param Collection|array $hashtags
* #return self
*/
public function addHashtags($hashtags)
{
foreach($hashtags as $hashtag){
$this->addHashtag($hashtag);
}
return $this;
}
/**
* Remove hashtag.
*
* #param Hashtag $hashtag
* #return self
*/
public function removeHashtag(Hashtag $hashtag)
{
$this->hashtags->removeElement($hashtag);
return $this;
}
/**
* Remove hashtags.
*
* #param Collection|array $hashtags
* #return self
*/
public function removeHashtags($hashtags)
{
foreach($hashtags as $hashtag){
$this->removeHashtag($hashtag);
}
return $this;
}

Paris ORM, has_many_through with restrictions

What's the best way to approach this in Paris ORM?
I have a set of categories and a set of supplier profiles that havw a column called reatured. Currently my class is as follows:
<?php
namespace {
/**
* Class Category
*/
class Category extends ConfettiModel
{
public static $_table = 'supplier_directory_category';
/**
* Returns only top level categories - they have no parent
*
* #return bool
*/
public static function topLevel()
{
return self::where('parent', 0);
}
public static function marketing()
{
return self::where('marketing', 'Yes');
}
public function getTable() {
return self::$_table;
}
/**
* Is this a top level category - has no parent
*
* #return bool
*/
public function isTopLevel()
{
return ($this->parentId == 0);
}
/**
* Associated DirectoryProfile's
*
* #return ORMWrapper
*/
public function profiles()
{
return $this->has_many_through('DirectoryProfile', 'CategoryDirectoryProfile', 'category', 'supplier');
}
}
I'd like to add a new function, featuredProfiles() that allows me to retrieve the same results as profiles(), but in this case I want to restrict it to suppliers with featured = 'Yes'.
I'm not quite sure how to make this happen.
I took a punt and the answer was easier than I anticipated:
public function featuredProfiles() {
return $this->profiles()->where('featured', 'Yes');
}
The where is added as part of the query on the joined table.

Categories