join tables in doctrine2 - php

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();

Related

Doctrine Query Base with grouping and latest record

I have a table "table_b" that contain the following details
I would like to use doctrine to query an output with a specific condition. Currently I'm using the block below to query.
$table_a= $em->getRepository('table_a')->findOneBy(['id'=>1]);
foreach($table_a->table_b as $records){
echo $records->name. " : " . $records->value;
}
It will output the entire ArrayCollection. Is there a way to query the record base on latest 'Date Created', that is base on the grouping of column 'Foreign Key Table 2'.
If you want to use native Doctrine query methods, you should use findOneBy with the order byparameter.
findOneBy(['id' => 1], ['DateCreated' => 'desc'])
Then, you says the result is an ArrayCollection, so using the ArrayCollection::first() method, you'll get the last created element
EDIT
Imagine you have a Group entity and a Member entity. groups table is your table_a and members table is your table_b.
Entity description should be something like that :
class Group
{
...
/**
* #ORM\OneToMany(targetEntity="Group", mappedBy="member", cascade={"persist", "remove", "merge"})
* #ORM\OrderBy({"dateCreated"="DESC"})
*/
protected $members;
...
public function __construct()
{
$members = new ArrayCollection();
}
// members handling accessors
/**
* #return ArrayCollection
*/
public function getMembers()
{
return $this->members;
}
/**
* #param $members
*
* #return $this
*/
public function setMembers($members)
{
$this->members = new ArrayCollection();
return $this->addMembers($members);
}
/**
* #param $members
*
* #return $this
*/
public function addMembers($members)
{
foreach ($members as $member)
{
$this->addMember($member);
}
return $this;
}
/**
* #param Member $member
*
* #return $this
*/
public function addMember(Member $member)
{
$this->members->add($member);
$member->setGroup($this);
return $this;
}
/**
* #param Member $member
*
* #return $this
*/
public function removeMember(Member $member)
{
if ($this->members->contains($member))
{
$this->members->removeElement($member);
}
return $this;
}
/**
* #param $members
*
* #return $this
*/
public function removeMembers($members)
{
foreach ($members as $member)
{
$this->removeMember($member);
}
return $this;
}
}
And Member entity :
class Member
{
/**
* #ORM\ManyToOne(targetEntity="Group", inversedBy="members")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $group;
/**
* #ORM\Column(type="datetime", name="date_created")
*/
protected $dateCreated;
/**
* #return Group
*/
public function getGroup()
{
return $this->group;
}
/**
* #param Group $group
*
* #return $this
*/
public function setGroup(Group $group)
{
$this->group = $group;
return $this;
}
}
Now, we have one group with a dateCreated ordered collection of members.
Example 1 : You want to get the last member created for a given group
$group = $em->getRepository(Group::class)->findOneBy(['id' => 1]);
$lastMember = $group->getMembers()->first();
Example 2 : You want to get all members created on 2014-01-30 :
$members = $group->getMembers()->filter(function (Member $member) {
return ($member->getDateCreated->format('Y-m-d') == '2014-01-30');
});
That's all folk !
PS : I haven't test this code
In TableRepository:
public function getLatestRecord()
{
return $this->getEntityManager()
->createQuery('SELECT t FROM MyBundle:MyEntity t GROUP BY t.table2NameField ORDER BY t.created DESC')
->setMaxResults(1)
->getOneOrNullResult();
}

How can I fetch rows using Docrine2 in a ManytoOne relationship

I have the following tables, where I have no problem in inserting, updating etc. But, how can I fetch the result rows for this kind of mapping?
Organizations
-->id
-->name
users
-->id
-->first_name
doctors
-->id
-->user_id
org_doctors
-->id
-->org_id
-->doctor_id
This is my OrgDoctor Entity:
<?php
namespace Doctor\Entity;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
use User\Entity\User;
use Doctor\Entity\Doctor;
use Organization\Entity\Organization;
/**
* #ORM\Entity
* #ORM\Table(name="org_doctors")
*/
class OrgDoctor extends BaseEntity{
/**
* #ORM\ManyToOne(targetEntity="Doctor\Entity\Doctor", inversedBy="orgDoctor")
* #ORM\JoinColumn(name="doctor_id",referencedColumnName="id",nullable=false)
*/
protected $doctor;
/**
* #ORM\ManyToOne(targetEntity="Organization\Entity\Organization", inversedBy="orgDoctor")
* #ORM\JoinColumn(name="org_id", referencedColumnName="id", nullable=false)
*/
protected $organization;
public function setDoctor(Doctor $doctor = null)
{
$this->doctor = $doctor;
return $this;
}
public function getDoctor()
{
return $this->doctor;
}
public function setOrganization(Organization $organization = null)
{
$this->organization = $organization;
return $this;
}
public function getOrganization()
{
return $this->organization;
}
}
And this is my Doctor Entity:
<?php
namespace Doctor\Entity;
use Library\Entity\BaseEntity;
use Users\Entity\User;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="doctors")
*/
class Doctor extends BaseEntity {
/**
* #ORM\OneToOne(targetEntity="Users\Entity\User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
* #var Users\Entity\User
*/
private $user;
/**
* #ORM\Column(name="summary", type="string")
* #var string
*/
private $summary;
function getUser() {
return $this->user;
}
function setUser(User $user) {
$this->user = $user;
}
function getSummary() {
return $this->summary;
}
function setSummary($summary) {
$this->summary = $summary;
}
}
This is how I'm fetching the result for a single doctor:
$doctor = $this->entityManager->find('Doctor\Entity\Doctor', (int) $doctorId);
How can I fetch rows from the OrgDoctor entity?
this is how i tried using queryBuilder:
$qb = $this->entityManager->createQueryBuilder();
$qb->select('od', 'd', 'o')
->from('Doctor\Entity\OrgDoctor', 'od')
->join('od.organization', 'o')
->join('od.doctor', 'd')
->where('od.organization = :organization')
->setParameter('organization', $orgId);
$query = $qb->getQuery();
$results = $query->getResult();
var_dump($results);
Notice: Undefined index: orgDoctor in C:\xampp\htdocs\corporate-wellness\vendor\doctrine\orm\lib\Doctrine\ORM\Internal\Hydration\ObjectHydrator.php on line 125
In Organization Entity:
/**
* #ORM\OneToMany(targetEntity="Doctor\Entity\OrgDoctor", mappedBy="organization")
*/
protected $orgDoctor;
Given your entity mapping, Doctrine should provide you with an out of the box repository for your OrgDoctor entity. That repository implements a few methods for you to retrieve entities of that type. One of them is findBy, which return arrays of OrgDoctor entities:
$this->getEntityManager()->getRepository(OrgDoctor::class)->findBy(['doctor' => $doctorId]));
$this->getEntityManager()->getRepository(OrgDoctor::class)->findBy(['organization' => $organizationId]));
$this->getEntityManager()->getRepository(OrgDoctor::class)->findBy(['doctor' => $doctorId, 'organization' => $organizationId]));
The last example, would be quite similar to findOneBy, which would return an OrgDoctor entity instead of an array:
$this->getEntityManager()->getRepository(OrgDoctor::class)->findOneBy(['doctor' => $doctorId, 'organization' => $organizationId]));
If you're planning to loop through the results and access their properties or other relationships, you might want to change the default repository strategy and define a custom repository for your OrgDoctor entity. What this will allow you to do, is to write your custom queries for retrieving your entities by means of a query builder class and DQL.
In order to avoid N+1 problems, you want to fetch join before a loop to fetch all the necessary associations in one go, so you won't be running N queries within your loop:
$qb->select('od', 'd', 'o')
->from(OrgDoctor::class, 'od')
->join('od.organization', 'o')
->join('od.doctor', 'd')
->where('od.doctor = :doctor')
->setParameter('doctor', $doctorId)
;

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();
}

Change the value of status column when adding a friend with Doctrine

I'm starting now with Doctrine 2 and Zend Framework, and I'm creating a system of friendship among the users.
In the table Friends of my database, I have the following columns:
User_id | Friend_id | Status
Where status is a number that can be -1, 0 or 1.
I'm using the relationship many-to-many, more specifically, the same shown in the documentation of Doctrine: http://www.doctrine-project.org/docs/orm/2.0/en/reference/association-mapping.html#many-to-many-self-referencing
But I (unfortunately) do not know how to modify the value of status column of the table friends. :(
How to proceed?
EDIT: The status column is inside the friends table, and not in the users table.
In this case you need to manually create the join table as an Entity:
<?php
/**
* #Entity
*/
class UserFriend
{
const STATUS_NORMAL = 1;
const STATUS_BANNED = 2;
const STATUS_ANOTHER_STATUS = 4;
/**
* #ManyToOne(targetEntity="User", inversedBy="userFriends")
*/
private $user;
/**
* #ManyToOne(targetEntity="User")
*/
private $friend;
/**
* #Column(type="integer");
*/
private $status = self::STATUS_NORNAL;
public function __construct(User $user, User $friend, $status)
{
$this->setUser($user);
$this->setFriend($friend);
$this->setStatus($status);
}
// ... getters and setters
}
/**
* #Entity
*/
class User
{
/**
* #OneToMany(targetEntity="UserFriend", mappedBy="user")
*/
private $userFriends;
public function __construct()
{
$this->userFriends = new ArrayCollection();
}
public function addFriend(User $friend, $status)
{
$this->userFriends->add(new UserFriend($this, $friend, $status));
}
public function getFriends()
{
$friends = array();
foreach($this->userFriends as $userFriend){
$friends[] = $userFriend->getFriend();
}
return $friends;
}
}
...That is a rough guide as to something you might consider.
class User
{
private $status;
public function setStatus($status) {
$this->status = $status;
return $this;
}
}
add this to your User entity
I've managed to solve this problem, thanks to #Cobby for the explication about the entity for the Join Table.
I'm still new to Doctrine, so surely this code can be improved, but it is what I have so far, and I hope it is useful to someone.
User Repository:
<?php
class UserRepository extends EntityRepository {
public function getFriends( \Entity\User $user, $status = Friends::STATUS_ACCEPTED )
{
return $this->getEntityManager()->createQuery( 'SELECT f FROM Entities\Friends f WHERE f.user = ?0 AND f.status = ?1' )
->setParameters( array( $user->getId(), 2 => $status ) )
->getResult();
}
}
Friendship Entity:
<?php
/**
* Friend Entity
*
* #Entity
* #Table(
* name="friendship",
* uniqueConstraints={
* #UniqueConstraint(name="UNIQ_FRIENDSHIP", columns={"user_id", "friend_id"})
* }
* )
*/
class Friendship
{
const STATUS_REQUESTED = 0;
const STATUS_ACCEPTED = 1;
const STATUS_REJECTED = -1;
const STATUS_BLOCKED = -2;
const STATUS_IGNORED = -3;
/**
* #Id #Column(type="integer")
* #GeneratedValue
*/
private $id;
/** #Column(type="integer") */
private $status;
/**
* #ManyToOne(targetEntity="User", inversedBy="userFriends")
*/
private $user;
/**
* #ManyToOne(targetEntity="User")
*/
private $friend;
/* ... */
Method inside some service class:
/**
* Creates a friendship between two users
* #param User $user
* #param User $friend
* #param integer $status
*
* #return Friends
*/
public function createOrUpdateFriendship( User $user, User $friend, $status )
{
//friendshipt already exists?
$friendship = $this->getRepository( 'Friends' )->findOneBy(array(
'user' => $user->getId(),
'friend' => $friend->getId()
));
if( $friendship ) {
$friendship->setStatus($status);
} else {
$entityManager = $this->getEntityManager();
$friendship = new Friends( $user, $friend, $status );
$entityManager->persist( $friendship );
$entityManager->flush();
}
return $friendship;
}
/* */

Categories