I've got this model:
/** #Entity #Table(name="articles") */
class Article {
/** #Id #GeneratedValue #Column(type="integer") */
protected $id;
/** #Column(type="string", length=100, nullable=true) */
protected $title;
/** #ManyToOne(targetEntity="User", inversedBy="articles") */
protected $author;
/** #Column(type="datetime") */
protected $datetime;
/**
* #ManyToMany(targetEntity="Game", inversedBy="articles")
* #JoinTable(name="articles_games",
* joinColumns={#JoinColumn(name="article_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="game_id", referencedColumnName="id")}
* )
*/
protected $games;
# Constructor
public function __construct() {
$this->datetime = new DateTime();
$this->games = new \Doctrine\Common\Collections\ArrayCollection();
}
# ID
public function getId() { return $this->id; }
# TITLE
public function setTitle($v) { $this->title = $v; }
public function getTitle() {
if(empty($this->title)) {
$game = $this->getFirstGame();
return ($game instanceof Game) ? $game->getTitle() : NULL;
} else
return $this->title;
}
# AUTHOR
public function setAuthor($v) { $this->author = $v; }
public function getAuthor() { return $this->author; }
# DATE & TIME
public function getDateTime() { return $this->datetime; }
public function setDateTime($v) { $this->datetime = $v; }
# GAMES
public function setGames($value) {
$except_txt = 'Jedna z przesłanych wartości nie jest instancją klasy Game!';
if(is_array($value)) {
foreach($value as $v) {
if($v instanceof Game) $this->games->add($v);
else throw new Exception($except_txt);
}
} else {
if($value instanceof Game) $this->games->add($value);
else throw new Exception($except_txt);
}
}
public function getGames() { return $this->games; }
}
How to make query looking like this
SELECT a FROM Article a WHERE :game_id IN a.games
I have this (the $game->getId() is an integer)
$articles = $db->createQuery("SELECT a.type FROM Article a WHERE :game_id IN a.games GROUP BY a.type")->setParameter('game_id', $game->getId())->getResult();
But it's returning me an syntax error
[Syntax Error] line 0, col 47: Error: Expected Doctrine\ORM\Query\Lexer::T_OPEN_PARENTHESIS, got 'a'
This question was linked from a more recent question that I just answered, and I feel it would also be beneficial to put it here as it is a much more correct solution:
The Doctrine IN function expects a format of (1, 2, 3, 4, ...) after the IN statement. Unfortunately, it is not meant for column conditionals to prove membership.
However, I believe you're looking for the MEMBER OF Doctrine function:
SELECT a FROM Article a WHERE :game_id MEMBER OF a.games
You can pass a valid Doctrine object or the identifier into game_id using this functionality.
The example for this is hidden deep in the Doctrine docs:
$query = $em->createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups');
$query->setParameter('groupId', $group);
$ids = $query->getResult();
If you are looking for articles related to one game:
$articles = $db->createQuery("SELECT a FROM Article a JOIN a.games game WHERE game.id = :game_id")
->setParameter('game_id', $game->getId())
->getResult();
or multiple:
$articles = $db->createQuery("SELECT a FROM Article a JOIN a.games game WHERE game.id IN (?,?, ... ?)")
->setParameters(array($game1->getId(), $game2->getId() ... $gameN->getId()))
->getResult();
I guess you need to create a custom repository for that. I have just solved such problem.
use Doctrine\ORM\EntityRepository;
class Company extends EntityRepository
{
/**
* Load by product.
* #param int $productId
* #return array
*/
public function getByProduct($productId)
{
$dql = "SELECT i FROM Domain\Model\Company i JOIN i.products p WHERE p.id = :id";
return $this->_em->createQuery($dql)->setParameter(':id', $productId)->getResult();
}
}
Related
Hello there have something i dont understand about SQL i guess. I linked below the code and the image about my db shema for help you to understand.
So my problem with this request is that it return an empty array and i dont understand why its not working as from my understanding it should return an array containing all the "r.messages" that have the specified ID related to the fields relation. What do i do wrong ?
PS: for some context helping your understanding, im trying to build a messagery system from a user to another. The SQL request i try to do should return the conversation between two users.
public function listOfMessages($value, $value2)
{
return $this->createQueryBuilder('user')
->select('r.message')
->innerJoin('user.sender', 's')
->innerJoin('user.receiver', 'r')
->where('s.sender = :value')
->andWhere('r.recipient = :value2')
->setParameter(':value', $value)
->setParameter(':value2', $value2)
->getQuery()
->getResult()
;
}
DB shema
Entity User
use App\Repository\PrivateMessageRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=PrivateMessageRepository::class)
*/
class PrivateMessage
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="sender")
*/
private $sender;
/**
* #ORM\ManyToOne(targetEntity=user::class, inversedBy="receiver")
*/
private $recipient;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $isRead = 0;
/**
* #ORM\Column(type="datetime_immutable", nullable=true)
*/
private $sentAt;
public function getId(): ?int
{
return $this->id;
}
public function getMessage(): ?string
{
return $this->message;
}
public function setMessage(?string $message): self
{
$this->message = $message;
return $this;
}
public function getSender(): ?User
{
return $this->sender;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
public function getRecipient(): ?user
{
return $this->recipient;
}
public function setRecipient(?user $recipient): self
{
$this->recipient = $recipient;
return $this;
}
public function getIsRead(): ?bool
{
return $this->isRead;
}
public function setIsRead(?bool $isRead): self
{
$this->isRead = $isRead;
return $this;
}
public function getSentAt(): ?\DateTimeImmutable
{
return $this->sentAt;
}
public function setSentAt(?\DateTimeImmutable $sentAt): self
{
$this->sentAt = $sentAt;
return $this;
}
}
Entity privateMessage
use App\Repository\PrivateMessageRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=PrivateMessageRepository::class)
*/
class PrivateMessage
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="sender")
*/
private $sender;
/**
* #ORM\ManyToOne(targetEntity=user::class, inversedBy="receiver")
*/
private $recipient;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $isRead = 0;
/**
* #ORM\Column(type="datetime_immutable", nullable=true)
*/
private $sentAt;
public function getId(): ?int
{
return $this->id;
}
public function getMessage(): ?string
{
return $this->message;
}
public function setMessage(?string $message): self
{
$this->message = $message;
return $this;
}
public function getSender(): ?User
{
return $this->sender;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
public function getRecipient(): ?user
{
return $this->recipient;
}
public function setRecipient(?user $recipient): self
{
$this->recipient = $recipient;
return $this;
}
public function getIsRead(): ?bool
{
return $this->isRead;
}
public function setIsRead(?bool $isRead): self
{
$this->isRead = $isRead;
return $this;
}
public function getSentAt(): ?\DateTimeImmutable
{
return $this->sentAt;
}
public function setSentAt(?\DateTimeImmutable $sentAt): self
{
$this->sentAt = $sentAt;
return $this;
}
}
Could you please use the web debug toolbar to extract the readable query and fire it agains your sql server.
I think your repository dql is wrong - hope i figure it out of the head correct.
You have to go over the message repository, not over the user repo.
You have to select the messages for user a and user b visaverce
$qb = $this->createQueryBuilder('m');
$qb->where(
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->eq('m.sender', ':sender'),
$qb->expr()->eq('m.recipient', ':recipient')
),
$qb->expr()->andX(
$qb->expr()->eq('m.sender', ':recipient'),
$qb->expr()->eq('m.recipient', ':sender')
)
)
)
->setParameter(':sender', $sender)
->setParameter(':recipient', $recipient)
->getQuery()
->getResult()
You say you want the conversation between the two users. Architectural i think, there is a missing table named conversation. Think about that:
A user can have a conversation to one-or-many users
A conversation can have one-or-many message(s)
A message can have a sender and a recipient
Maybe a better solution if you want to have more than one conversation ...
Thank you for your time and tips, i tried your dql request and it return "NULL".
About doing it into the message repo instead of user repo i did it like this because in my entity user i have two methods that can retrieve messages sent and received.
But anyways even trying it inside message repo return an empty array.
I tryed with rawsql and got some good result:
public function stack($id)
{
$rawSql =
"SELECT pm.sent_at as date, pm.message, user.login as login FROM private_message as pm
INNER JOIN user on user.id = pm.sender_id
WHERE pm.sender_id = $id
UNION
SELECT pm.sent_at as date, pm.message, user.login as login FROM private_message as pm
INNER JOIN user on user.id = pm.recipient_id
WHERE pm.recipient_id = $id
ORDER BY date DESC"
;
$conn = $this->getEntityManager()->getConnection()->prepare($rawSql);
$stmt = $conn->executeQuery([$rawSql]);
return $stmt->fetchAll();
}
However this request have some weird behavior specially when i set two different $id variable if i manually set the user id and recipient id it start to duplicate some messages and also the user.login returned is the same for all messages sent and recieved. But still a kind of progress as i have the conversation with this sql request but its not a good solution for long term as the ->fetchAll() method is depreciated and going to be removed from doctrine api in 2023.
By the way the clause UNION in my sql request seem to not have its equal version with DQL do you have any tips to make a UNION with a DQL request ?
I think you are right about adding a conversation table ill have to think back about my db architecture maybe it will make the process more simple and intuitive
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();
}
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();
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();
}
I have this DB model:
Then I made this entities (I just leave the relation part since the other isn't relevant on the topic):
Orders.php
class Orders {
/**
* #ORM\ManyToOne(targetEntity="Person", inversedBy="orders")
* #ORM\JoinColumn(name="person_id", referencedColumnName="id")
* */
protected $person;
public function setPerson(Person $person)
{
$this->person = $person;
return $this;
}
public function getPerson()
{
return $this->person;
}
}
Person.php
class Person {
/**
* #ORM\OneToMany(targetEntity="NaturalPerson", mappedBy="person")
* */
private $naturals;
/**
* #ORM\OneToMany(targetEntity="LegalPerson", mappedBy="person")
* */
private $legals;
/**
* #ORM\OneToMany(targetEntity="Orders", mappedBy="person")
* */
private $orders;
public function __construct()
{
$this->naturals = new ArrayCollection();
$this->legals = new ArrayCollection();
$this->orders = new ArrayCollection();
}
public function getNaturals()
{
return $this->naturals;
}
public function getLegals()
{
return $this->legals;
}
public function getOrders()
{
return $this->orders;
}
}
NaturalPerson.php
class NaturalPerson {
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Person", inversedBy="naturals")
* #ORM\JoinColumn(name="person_id", referencedColumnName="id")
*/
protected $person;
/**
* #ORM\Column(name="identification_type", type="ci_type", nullable=false)
* #DoctrineAssert\Enum(entity="Tanane\FrontendBundle\DBAL\Types\CIType")
*/
protected $identification_type;
/**
* #ORM\Column(name="ci", type="integer", nullable=false)
*/
protected $ci;
public function setPerson(Person $person)
{
$this->person = $person;
return $this;
}
public function getPerson()
{
return $this->person;
}
public function setIdentificationType($identification_type)
{
$this->identification_type = $identification_type;
return $this;
}
public function getIdentificationType()
{
return $this->identification_type;
}
public function setCI($ci)
{
$this->ci = $ci;
return $this;
}
public function getCI()
{
return $this->ci;
}
}
I omitted LegalPerson since it's pretty much the same as NaturalPerson so here is the problem. The mapping looks good but how I do get related records from Orders?
The idea behind this is for each Orders I need to know to which Person belongs too (the Orders) and also the extra information stored at NaturalPerson or LegalPerson depending on person.type.
See this code:
public function getOrdersAction()
{
$response = array();
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository("FrontendBundle:Orders")->findAll();
if (!$entities)
{
$response['message'] = "No se encontraron resultados";
}
$orders = array();
foreach ($entities as $entity)
{
$personType = $entity->getPerson()->getPersonType();
$order = array();
$order[] = $entity->getNickname();
// Here I'm trying to access to `Naturals` methods from `Orders`
if ($personType == 1)
{
$order[] = $entity->getPerson()->getNaturals()[0]->getIdentificationType() . $entity->getPerson()->getNaturals()[0]->getCI();
}
elseif ($personType == 2)
{
$order[] = $entity->getPerson()->getLegals()[0]->getIdentificationType() . $entity->getPerson()->getLegals()[0]->getRIF();
}
$orders[] = $order;
}
$response['data'] = $orders;
return new JsonResponse($response);
}
But I get this error:
Error: Call to a member function getIdentificationType() on a
non-object in
/var/www/html/tanane/src/Tanane/BackendBundle/Controller/OrderController.php
line 115
Maybe my mapping is wrong since I should have OneToOne between Person and NaturalPerson (and that sounds wrong to my logic as DER shows) or maybe is not, but then I don't know how to fetch related properties for just one record, I read docs here and also in here but they didn't talk about this part or I don't see it, any advice? ideas? tips?
Trying to use Repositories and DQL to solve the problem
I'm building a function in a Repository class to fetch the data and not get to complicated as apparently my problem is, so I did this:
public function getOrders($person_type = 1)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('ord.*, ps.*')
->from("FrontendBundle:Orders", "ord")
->join('FrontendBUndle:Person', 'ps', 'WITH', 'ps.id = ord.person_id')
->orderBy('ord.created', 'DESC');
if ($person_type == 1)
{
$qb
->select('np.*')
->join('FrontendBundle:NaturalPerson', 'np', 'WITH', 'ps.id = np.person'); // Join NaturalPerson table
}
elseif ($person_type == 2)
{
$qb
->select('lp.*')
->join('FrontendBundle:LegalPerson', 'lp', 'WITH', 'ps.id = lp.person'); // Join NaturalPerson table
}
return $qb->getQuery()->getResult();
}
I'm not tested yet so maybe it won't works but, if the idea is to get the extra information for both tables, then using this DQL I made how I pass the $person_type which is inside Person table? This is getting a little complicated, at least for me
Running a raw query to see if columns are NULL
I build this simple query just for test if results are NULL:
SELECT
ord.id,
ord.person_id as ord_person_id,
ord.nickname,
ps.id,
ps.description,
np.person_id as natural_person_id,
np.identification_type,
np.ci
FROM
orders ord
LEFT JOIN person ps ON ord.person_id = ps.id
LEFT JOIN natural_person np ON np.person_id = ps.id
WHERE
ps.person_type = 1;
And this what query returns:
So there is not NULL columns in there
CRUD for create new Orders
// Set Person entity
$entityPerson = new Person();
$person_type === 1 ? $entityPerson->setDescription($orders['nat']['person']['description']) : $entityPerson->setDescription($orders['leg']['person']['description']);
$person_type === 1 ? $entityPerson->setContactPerson($orders['nat']['person']['contact_person']) : $entityPerson->setContactPerson($orders['leg']['person']['contact_person']);
$entityPerson->setPersonType($person_type);
$em->persist($entityPerson);
$em->flush();
...
if ($person_type === 1)
{
// Set NaturalPerson entity
$entityNatural = new NaturalPerson();
$entityNatural->setIdentificationType($orders['nat']['identification_type']);
$entityNatural->setCI($orders['nat']['ci']);
$em->persist($entityNatural);
$em->flush();
}
elseif ($person_type === 2)
{
// Set LegalPerson entity
$entityLegal = new LegalPerson();
$entityLegal->setIdentificationType($orders['leg']['identification_type']);
$entityLegal->setRIF($orders['leg']['rif']);
$em->persist($entityLegal);
$em->flush();
}
Since LegalPerson and NaturalPerson are specializations of Person I would recommend using what Doctrine calls Class Table Inheritance (documentation).
You would have:
Person.php
/**
* #ORM\Table(name="person")
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({
* "natural" = "NaturalPerson",
* "legal" = "LegalPerson",
* })
*/
class Person {
/**
* #ORM\OneToMany(targetEntity="Orders", mappedBy="person")
* */
private $orders;
public function __construct()
{
$this->orders = new ArrayCollection();
}
public function getOrders()
{
return $this->orders;
}
}
NaturalPerson.php
/**
* #ORM\Table(name="natural_person")
* #ORM\Entity
*/
class NaturalPerson extends Person {
/**
* #ORM\Column(name="identification_type", type="ci_type", nullable=false)
* #DoctrineAssert\Enum(entity="Tanane\FrontendBundle\DBAL\Types\CIType")
*/
protected $identification_type;
/**
* #ORM\Column(name="ci", type="integer", nullable=false)
*/
protected $ci;
public function setIdentificationType($identification_type)
{
$this->identification_type = $identification_type;
return $this;
}
public function getIdentificationType()
{
return $this->identification_type;
}
public function setCI($ci)
{
$this->ci = $ci;
return $this;
}
public function getCI()
{
return $this->ci;
}
}
Order.php stays the same.
As you can see, now both NaturalPerson and LegalPerson extend Person. Since you've changed your entities definition, you'll have to update your database schema.
Now, in your Controller you only have to do this:
foreach ($entities as $entity)
{
$person = $entity->getPerson();
$order = array();
$order[] = $entity->getNickname();
if ($person instanceof NaturalPerson)
{
$order[] = $person->getIdentificationType() . $person->getCI();
}
else // it has to be LegalPerson
{
$order[] = $person->getIdentificationType() . $person->getRIF();
}
$orders[] = $order;
}
Don't forget to add the use statement for NaturalPerson!
This way you only work with instances of either NaturalPerson or LegalPerson. I'm sure you can further improve this.
Lastly, you will have to change your CRUD for this. You don't work directly with Person anymore (in fact, it should be abstract), so now you need to handle CRUD for NaturalPerson and for LegalPerson separately. Each will have its Type, Controller, views, etc.
Your code would now look like this:
if ($person_type === 1)
{
$entityPerson = new NaturalPerson();
$entityPerson->setDescription($orders['nat']['person']['description']);
$entityPerson->setContactPerson($orders['nat']['person']['contact_person']);
$entityPerson->setIdentificationType($orders['nat']['identification_type']);
$entityPerson->setCI($orders['nat']['ci']);
$em->persist($entityPerson);
$em->flush();
}
elseif ($person_type === 2)
{
$entityPerson = new LegalPerson();
$entityPerson->setDescription($orders['leg']['person']['description']);
$entityPerson->setContactPerson($orders['leg']['person']['contact_person']);
$entityPerson->setIdentificationType($orders['leg']['identification_type']);
$entityPerson->setRIF($orders['leg']['rif']);
$em->persist($entityPerson);
$em->flush();
}
Perhaps, a problem in other. You can forget to assign NaturalPerson or LegalPerson to Person entity. So you need to check it before calling getIdentificationType():
if($personType == 1){
if(null !== $natural = $entity->getPerson()->getNaturals()[0]){
$order[] = $natural->getIdentificationType() . $natural->getCI();
}
}elseif($personType == 2){
if(null !== $legal = $entity->getPerson()->getLegals()[0]){
$order[] = $legal->getIdentificationType() . $legal->getRIF();
}
}