Symfony Doctrine : fetch a collection persisted as array - php

I have two entities Modules and Orders where One order have Many modules and I'm wondering how to fetch an array collection of modules persisted as follow:
Table: Orders
id | modules | user_id | ... | created_at |
----------------------------------------------------
1 | [2,6,5] | 12 | ... | 2018-07-28 00:00:00 |
----------------------------------------------------
As you can see my modules are persisted as array. So after that how can I make Doctrine (with Symfony) to get my modules

I think you need a ManyToOne relationShip ... as I know, we never store an array in database.
in your example order can have many modules and module can have just one order ...
in this case order called owning side and module called invers side ...
and module keep id of order ...
look at this example
Table: Orders
id | user_id | ... | created_at |
----------------------------------------------------
1 | 12 | ... | 2018-07-28 00:00:00 |
----------------------------------------------------
Table: Modules
id | order_id | ... | created_at |
----------------------------------------------------
1 | 1 | ... | 2018-07-28 00:00:00 |
----------------------------------------------------
2 | 1 | ... | 2018-07-29 00:00:00 |
----------------------------------------------------
you must write your code like this...
Order Class
class Order implements OrderInterface
{
/**
* #var Collection
*
* #ORM\OneToMany(targetEntity="Module", mappedBy="order", cascade={"persist"})
*/
protected $modules;
/**
* Don't forget initial your collection property
* Order constructor.
*/
public function __construct()
{
$this->modules = new ArrayCollection();
}
/**
* #return Collection
*/
public function getModules(): Collection
{
return $this->modules;
}
/**
* #param ModuleInterface $module
*/
public function addModule(ModuleInterface $module): void
{
if ($this->getModules()->contains($module)) {
return;
} else {
$this->getModules()->add($module);
$module->setOrder($this);
}
}
/**
* #param ModuleInterface $module
*/
public function removeModule(ModuleInterface $module): void
{
if (!$this->getModules()->contains($module)) {
return;
} else {
$this->getModules()->removeElement($module);
$module->removeOrder($this);
}
}
}
Module Class
class Module implements ModuleInterface
{
/**
* #var OrderInterface
*
* #ORM\OneToMany(targetEntity="Order", mappedBy="modules", cascade={"persist"})
*/
protected $order;
/**
* #param OrderInterface $order
*/
public function setOrder(OrderInterface $order)
{
$this->order = order;
}
public function getOrder(): OrderInterface
{
return $this->order;
}
}
when you persist order object by doctrine... doctrine handle this and create items

Related

Laravel global access with polymorphic relations

In my application I have two columns employable_id && employable_type in almost every table, which are used for storing the information about the user who has created the record.
Something like this:
subscribers: products:
------------------------------------ ------------------------------------
id | employable_id | employable_type id | employable_id | employable_type
------------------------------------ ------------------------------------
1 | 1 | App\Company 1 | 1 | App\Company
2 | 1 | App\Company 2 | 1 | App\Employee
3 | 1 | App\Employee 3 | 3 | App\Employee
4 | 1 | App\Employee 4 | 8 | App\Employee and more...
Subscriber.php
class Subscriber extends Model
{
/**
* Polymorphic relations
*/
public function employable()
{
return $this->morphTo();
}
}
I have created an accessor to combine polymorphic relationship's columns, which is working fine.
class Subscriber extends Model
{
/**
* Polymorphic relations
*/
public function employable()
{
return $this->morphTo();
}
/**
* Accessor
*/
public function getAddedByAttribute()
{
return $this->employable->name . ' [ ' . $this->employable->designation . ' ]';
}
}
class Product extends Model
{
/**
* Polymorphic relations
*/
public function employable()
{
return $this->morphTo();
}
/**
* Accessor
*/
public function getAddedByAttribute()
{
return $this->employable->name . ' [ ' . $this->employable->designation . ' ]';
}
}
I am trying to make the getAddedByAttribute method global instead of adding in every model class.
How can I do that..?
you can make a trait and use it in every model need this function

Doctrine Distinct Count [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
What is the best practice for retrieving the DISTINCT COUNT on an entity collection?
In this example entity (Customer), I have a oneToMany relationship with Orders.
I want to count how many sales & products the customer has ordered:
> select * from orders;
+----------+----------+----------+
| customer | sale_ref | prod_ref |
+----------+----------+----------+
| 1 | sale_1 | prod_1 |
| 1 | sale_1 | prod_2 |
| 1 | sale_2 | prod_1 |
| 1 | sale_3 | prod_3 |
+----------+----------+----------+
> select count(prod_ref) from order where customer = 1;
+-----------------+
| count(prod_ref) |
+-----------------+
| 4 |
+-----------------+
> select count(distinct(sale_ref)) from order where customer = 1;
+-----------------+
| count(prod_ref) |
+-----------------+
| 3 |
+-----------------+
Here is the code
use Doctrine\ORM\Mapping as ORM;
class Customer
{
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\OneToMany(targetEntity="Orders", mappedBy="customer", cascade={"persist", "remove"}, fetch="EXTRA_LAZY")
*/
protected $orders;
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getOrders(): \Doctrine\Common\Collections\Collection
{
return $this->orders;
}
/**
* #return int
*/
public function getOrdersProductCount(): int
{
return $this->orders->count();
}
}
class Orders
{
/**
* #var Customer $customer
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="orders")
*/
protected $customer;
/**
* Non-unique sales reference
* #var string $salesRef
* #ORM\Column(name="sales_ref", type="string")
*/
protected $salesRef;
/**
* Unique product reference
* #var string $productRef
* #ORM\Column(name="product_ref", type="string")
*/
protected $productRef;
/**
* #return Customer
*/
public function getCustomer(): Customer
{
return $this->customer;
}
/**
* #return string
*/
public function getProductRef(): string
{
return $this->productRef;
}
/**
* #return string
*/
public function getSalesRef(): string
{
return $this->salesRef;
}
}
Using the Customer->getOrdersProductCount() works perfectly fine for retrieving the product count and is said to be "good practice" as it doesn't hit the database with full loading of the collection:
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/extra-lazy-associations.html
If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection Collection#count()
However, in this example, a Customer can have multiple products for a sale - where the salesRef is non-unique. What is the best method for retrieving a DISTINCT COUNT of the salesRef?
This could/should be handled in the entity repository class:
class OrdersRepository
{
public function getSalesCount($customer): int
{
return (int)$this->createQueryBuilder('o')
->select('COUNT(DISTINCT(o.salesRef))')
->where('o.customer = :customer')
->setParameter('customer', $customer)
->setMaxResults(1)
->getQuery()
->getSingleScalarResult();
}
public function getProductCount($customer): int
{
return (int)$this->createQueryBuilder('o')
->select('COUNT(o.productRef)')
->where('o.customer = :customer')
->setParameter('customer', $customer)
->setMaxResults(1)
->getQuery()
->getSingleScalarResult();
}
}
This works BUT I need to load the entityManager/CustomerRepository in order to access these methods - whereas at least I can retrieve the product count from within the entity....
How could I access the distinct sales count from within the Customer entity - if at all?
I have considered using the Collection#filter() method and/or looping through the Orders entity to create an array with the salesRef as the key and then using array_unique#count() but this doesn't seem "right" - I suspect I know the answer (use the entity repository) but I would prefer to be able to access the sales count from within the Customer entity - what is the best practice/method?
I think this should do it and would be a more portable way of doing it. I did not test it though.
$qb = $this->createQueryBuilder('o');
return (int)$qb
->select($qb->expr()->countDistinct('o.salesRef'))
->where('o.customer = :customer')
->setParameter('o.customer', $customer)
->setMaxResults(1)
->getQuery()
->getSingleScalarResult();
Reference is here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/query-builder.html#the-querybuilder
Hope this helps

How can I retrieve data from ArrayCollection (Symfony)?

animals:
| id | name |
|----|------|
| 1 | cat |
| 2 | dog |
| 3 | frog |
category:
| id | name |
|----|--------|
| 1 | green |
| 2 | blue |
| 3 | orange |
animals_category:
| animals_id | category_id |
|------------|-------------|
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
What I want to do is get the categories for dog:
green, blue
This is my approach:
Controller:
$id = '2';
$result = $this->getDoctrine()->getRepository('Animals:Category')->findByIdJoinedToCategory(['id'=>$id]);
Animals Repository:
public function findByIdJoinedToCategory()
{
$query = $this->getEntityManager()
->createQuery(
'SELECT a, b FROM Animals:Category a
JOIN a.category b');
try {
return $query->getResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
But I get an error message:
Unknown Entity namespace alias 'Animals'.
entity Animals:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="App\Repository\AnimalsRepository")
*/
class Animals
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="Category")
* #ORM\JoinColumn(name="category", referencedColumnName="id")
*/
private $category;
public function getId(): ?int
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getCategory()
{
return $this->category;
}
public function setCategory($category): self
{
$this->category = $category;
return $this;
}
public function addCategory(Category $category): self
{
$this->category[] = $category;
return $this;
}
public function __construct()
{
$this->category = new ArrayCollection();
}
}
There's no Animals:Category entity. You have entities Animals and Category.
The correct answer depends if you're using Symfony 3 or 4, because Symfony 3 uses entity aliases (namespacing with : notation which you're trying ot use), while Symfony 4 prefers full qualified namespace (\App\Entity\Animals).
So, first mistake is in line where you're trying to get repository:
getRepository('Animals:Category')
And the second in findByIdJoinedToCategory() in DQL query :
'SELECT a, b FROM Animals:Category a
JOIN a.category b'
Now solutions:
Symfony 3
Since it looks you don't have any bundles (I guess it's Symfony 4 but whatever), you don't have any entity namespace alias, so you should simply use its name.
getRepository('Animals')
Now, I assume, that with a you want to reference Animals entity/table, so it should be
'SELECT a, b FROM Animals a
JOIN a.category b'
Symfony 4
If you use Symfony 4, then use should use entity FQNS as entity name (App\Entity\Animals).
So it would be
getRepository('\App\Entity\Animals')
or
getRepository(\App\Entity\Animals::class)
to get repository. The second one is better, because it will be easier to refactor when needed (IDE will be able to find usage of class).
And in query it would be
'SELECT a, b FROM App\Entity\Animals a
JOIN a.category b'
or if you would like to avoid using hardcoded string class names:
'SELECT a, b FROM ' . \App\Entity\Animals:class . ' a
JOIN a.category b'

Phalcon model is not recognizing AUTO_INCREMENT table column

The current table Structure is:
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| title | varchar(25) | NO | UNI | NULL | |
| content | varchar(500) | NO | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
Model:
<?php
namespace Com\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Uniqueness;
class Articles extends Model
{
/**
*
* #var integer
*/
public $id;
/**
*
* #var string
*/
public $title;
/**
*
* #var string
*/
public $content;
/**
* Returns table name mapped in the model.
*
* #return string
*/
public function getSource()
{
return 'articles';
}
/**
* Allows to query a set of records that match the specified conditions
*
* #param mixed $parameters
* #return Articles[]
*/
public static function find($parameters = null)
{
return parent::find($parameters);
}
/**
* Allows to query the first record that match the specified conditions
*
* #param mixed $parameters
* #return Articles
*/
public static function findFirst($parameters = null)
{
return parent::findFirst($parameters);
}
Upon saving data from controller, I am receiving this error: id is required
EDIT
Here is the snippet for the save method:
$article = new Articles();
$article->title = $this->request->getPost('title', 'striptags');
$article->content = $this->request->getPost('content');
if (!$article->save()) {
$this->flash->error($article->getMessages());
} else {
$this->flash->success("Article created.");
Tag::resetInput();
}
I've never had a problem with auto increment fields and Phalcon.
Only thing I can suggest is setting default to AUTO_INCREMENT
delete cache file
ex. "~\cache\metaData\meta-bds_models_yourmodel-your_model.php"

How can I add field on the fly in symfony2 entity

I would like to know if exist a way to add fields on the fly to any entity on Symfony2. I'm searching on the big internet and I didn't find anything. When I said "a way", I mean if exist a Doctrine Extension with that behavior, a bundle that implement it, design pattern, etc.
My idea is something similar to Translatable behavior of Doctrine Extensions. Supouse I have a Address entity, so I would like to add some attributes on the fly like street, number, intersections, and others but at the begining I didn't know what fields could exist.
I'm thinking something as 2 entities: Address and AddressFieldValues. Address will have specifics attributes like id, foreing keys of relationships with others classess and will be used to inject the dynamic attributes (a collections of field-values). AddressFieldValue will have the reals fields-values of Address, with the following attributes: id, address_id, field_name, field_value.
So, entity Address could be like this:
/**
* Address
*
* #ORM\Entity(repositoryClass="AddressRepository")
* #ORM\Table(name="address")
*/
class Address
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(
* targetEntity="AddressFieldValues",
* mappedBy="object",
* cascade={"persist", "remove"}
* )
*/
private $field_value;
public function __construct()
{
$this->field_value = new ArrayCollection();
}
public function getFieldValue()
{
return $this->field_value;
}
public function addFieldValue(AddressFieldValues $fv)
{
if (!$this->field_value->contains($fv)) {
$this->field_value[] = $fv;
$fv->setObject($this);
}
}
public function getId()
{
return $this->id;
}
}
and AddressFieldValues entity could be like this:
/**
* #ORM\Entity
* #ORM\Table(name="address_field_values",
* uniqueConstraints={#ORM\UniqueConstraint(name="lookup_unique_idx", columns={
* "object_id", "field"
* })}
* )
*/
class AddressFieldValues
{
/**
* #var integer $id
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue
*/
protected $id;
/**
* #var string $field
*
* #ORM\Column(type="string", length=32)
*/
protected $field;
/**
* #ORM\ManyToOne(targetEntity="Address", inversedBy="field_value")
* #ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $object;
/**
* #var string $content
*
* #ORM\Column(type="text", nullable=true)
*/
protected $content;
/**
* Convenient constructor
*
* #param string $field
* #param string $value
*/
public function __construct($field, $value)
{
$this->setField($field);
$this->setContent($value);
}
/**
* Get id
*
* #return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* Set field
*
* #param string $field
*/
public function setField($field)
{
$this->field = $field;
return $this;
}
/**
* Get field
*
* #return string $field
*/
public function getField()
{
return $this->field;
}
/**
* Set object related
*
* #param string $object
*/
public function setObject($object)
{
$this->object = $object;
return $this;
}
/**
* Get related object
*
* #return object $object
*/
public function getObject()
{
return $this->object;
}
/**
* Set content
*
* #param string $content
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string $content
*/
public function getContent()
{
return $this->content;
}
}
So, if I have the following values on table: address_field_values
id | object | field | content
1 | 1 | street | 1st Ave
2 | 1 | number | 12345
3 | 1 | intersections | 2sd Ave and 4th Ave
4 | 2 | street | 1st Ave
5 | 2 | number | 12347
6 | 2 | intersections | 2sd Ave and 4th Ave
7 | 3 | street | 1st Ave
8 | 3 | number | 12349
9 | 3 | intersections | 2sd Ave and 4th Ave
For now address table only have the following values:
| id |
| 1 |
| 2 |
| 3 |
I could like to inject those fields-values to a Address object on the fly, to do something like this:
// if I need get de Address with id = 2
$addressRepository = $em->getRepository('Address');
$address = $addressRepository->find(2);
sprintf('The address is: "%s", #"%s" between "%s".', $address->getStreet(), $address->getNumber(), $address->getIntersections());
// then it should show: The address is 1st Ave, #12347 between 2sd Ave and 4th Ave.
//
// or if I need add a new Address, do something like this:
$address = new Address();
$address->setStreet('1st Ave');
$address->setNumber('12351');
$address->setIntersections('2sd Ave and 4th Ave');
$em->persist($address);
$em->flush();
then it save the address and address_field_values, and the tables have the following values:
// address
| id |
| 1 |
| 2 |
| 3 |
| 4 |
// address_field_values
id | object | field | content
1 | 1 | street | 1st Ave
2 | 1 | number | 12345
3 | 1 | intersections | 2sd Ave and 4th Ave
4 | 2 | street | 1st Ave
5 | 2 | number | 12347
6 | 2 | intersections | 2sd Ave and 4th Ave
7 | 3 | street | 1st Ave
8 | 3 | number | 12349
9 | 3 | intersections | 2sd Ave and 4th Ave
10 | 4 | street | 1st Ave
11 | 4 | number | 12351
12 | 4 | intersections | 2sd Ave and 4th Ave
So, any ideas how can I do that?
Remember, I have as requirement in my bussiness logic that I didn't know what fields could have a Address at beginig so I need to inject the fields on the fly. I use Address as example but this behavior can be used for any entity.
Thanks in advance
I think that your request is similar to a collection in a form (Doctrine2 documentation).
In the documentation, a collection of Tags entities with name property) is linked to a Task entity. In your case, the entity AddressFieldValue will have the field and content properties and the collection of AddressFieldValue entities will be added to Address entity.
So, by using this documentation and replacing Task by Address and Tag by AddressFieldValue it should works.

Categories