limit columns returned in relationnal entity symfony2 - symfony

Is it possible to filter an entity and display only few columns in symfony2?
I think I can do a custom query for this, but it seems a bit dirty and I am sure there is a better solution.
For example I have my variable $createdBy below, and it contains few data that shouldnt be displayed in this parent entity such as password etc...
/**
* #var Customer
*
* #ORM\ManyToOne(targetEntity="MyCompany\Bundle\CustomerBundle\Entity\Customer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="created_by", referencedColumnName="id", nullable=false)
* })
*/
protected $createdBy;
So I need to display my Customer entity, but only containing fields like id and name for example.
EDIT :
I already have an instance of Project, the entity with my createdBy field, and I want to grab my customer data 'formatted' for this entity and not returning too much fields like password ...
Thanks

It sounds like expected behavior to me. The doctrine documentation seems to imply that eager fetching is only one level deep.
According to the docs:
Whenever you query for an entity that has persistent associations and
these associations are mapped as EAGER, they will automatically be
loaded together with the entity being queried and is thus immediately
available to your application.
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
The entity being queried has eager on createdBy so it will be populated.
to bypass you can create a method in your entity repository as following :
// join entities and load wanted fields
public function findCustom()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
hope this helps you
try this and let me know if it works, you should fill the right repository name
'XXXXBundle:CustomerYYYY', 'c'
public function findUser($user_id){
$qb = $this->_em->createQueryBuilder('c')
->select(array('c', 'cb.id', 'cb.name'))
->from('XXXXBundle:Customer', 'c')
->where('c.id <> :id')
->leftJoin('c.createdBy', 'cb')
->setParameter('id', $user_id)->getQuery();
if ($qb != null)
return $qb->getOneOrNullResult();
return null;
}

Related

Doctrine saves entities twice

Iam using Symfony(4.3) and Doctrine. When I want to get a user like this in the constructor of the script:
$userRepo = $this->em->getRepository(User::class);
$this->systemUser = $userRepo->findOneBy([
"firstName" => "system",
"lastName" => "system",
]);
And save the reference to this user for example at a createdBy-field like this:
$newUser->setName("test");
$newUser->setCreatedBy($this->systemUser);
$this->entityManager->persist($newUser);
$this->entityManager->flush();
The systemUser is persisted to the database after every flush().
I already tried to get only the reference with
$this->systemUser = $this->entityManager->getReference(User::class, $this->systemUser->getId());
But this doesn't work either.
Edit:
The setCreatedBy-Method:
public function setCreatedBy(User $user): void {
$this->createdBy = $user;
}
The 'createdBy'-Field:
/**
* #var User
* #ORM\ManyToOne(targetEntity="App\Entity\User", cascade={"persist"})
* #ORM\JoinColumn(nullable=true)
*/
protected $createdBy;
You have configured the cascade={"persist"} option for the relation which means Doctrine will save "new" entities found through the relation.
As your question suggests the createdBy field can only contain a relationship to already existing users. This means cascade is definitely not necessary here. It's also the reason why existing entities are persisted again in your case.
Remove the cascade option from the mapping-configuration for the property to resolve your issue.
Clear your cache afterwards.
To answer my own question:
I forgot to mention that I insert ~ 70.000 items in a loop and use clear() every 100 rows. The problem is the use of the entity manager's clear()-method. it detaches all doctrine-managed entites so doctrine thinks that these entities are new and saves them to the db. So I have to do it like this:
$newUser->setCreatedBy($this->em->getReference(User::class, $this->systemUser->getId()));

Doctrine get all entities from collection

I don't know if what I am trying is really possible. So I thought to ask it to you guys.
What I am trying to do:
get a set of companies
get all the users associated with the given companies
In code:
$companyIds = array(1,2,3);
$companies = $this->em->getRepository('AppBundle:Company')->findById($companyIds);
dump($companies->getUsers()); // this will not work, but I like it to work
Where they are associated as follows:
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Company", inversedBy="users")
* #ORM\JoinColumn(name="company_id", referencedColumnName="id", nullable=false)
*/
private $company;
}
class Company
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\User", mappedBy="company")
*/
private $users;
}
Repository returns you an ArrayCollection of entities, not a single entity, therefore you need to access each of them separately.
This should work:
foreach($companies as $company) {
$company->getUsers();
}
The problem with the above is that by default it will fetch (lazy load) users from database for each company in separate query (on demand when calling getUsers), which would be very inefficient on larger scale.
There are couple possible solution depending on your needs.
You could configure doctrine to always fetch users with companies, which is called eager fetch.
Having fetched users, you can merge ArrayCollections (and remove duplicates if needed), to achieve single collection containing all users.
Other way could be to fetch companies with users by creating sufficient DQL query in a custom method of you company's repository. If you need only users and don't need companies, then it could be a query that only fetches users without companies.
Try something like this in your User-Repository:
public function getAllUsersFromCompanies($ids)
{
$qb = $this->createQueryBuilder('u');
$qb->leftJoin('u.company', 'c')
->where('c.id IN :ids')
->setParameter('ids', $ids)
->getQuery();
return $query->getResult();
}
We are joining the user table with the company table here, which gives us the company for each user. Then, we filter out every user, that has the wrong company.
You can e.g. fetch all Comapany entities with users with one query:
$companies = $em->createQueryBuilder()
->select('c, u')
->from('AppBundle:Company', 'c')
// or left join based on you needs
->innerJoin('c.users', 'u')
->getQuery()
->getResult();
This will not result in queries when fetching company users.

Custom SELECT for findBy in Doctrine

I intend to build multilingual website. So I have DB table with columns:
id
text_en
text_pl
When getting data from DB I'd like to have only id and text fields. Normally, I'd issue:
SELECT id, text_$locale AS text FROM page ...
How to do it in Doctrine so that I have such SELECT done automatically when I use findBy() or findOneBy()? Is Query Builder my only option?
Creating a query is the semantic option; you should put it in as a custom method in a repository.
I'm posting an example here so that I can use it for reference later on.
Quick example:
<?php
namespace Acme\AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
/**
* PageRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class PageRepository extends EntityRepository
{
/**
* Gets and id and page name based by ID
* #return array
*/
public function findNameById($id)
{
$query = $this->getEntityManager()
->createQuery("SELECT p.id, p.name
FROM Acme\AppBundle\Entity\Page p
WHERE p.id = :pageId");
$query->setParameter('pageId', $id);
$result = $query->getOneOrNullResult();
return $result;
}
}
Correct, you'll have to use a querybuilder or native query if you want to select specific columns.
Specifying individual columns using findBy is not possible. You could lazy-load associations to entities in other tables but at a minimum findBy will return all columns from the entity's table.

Doctrine Query Builder Where Count of ManyToMany is greater than

Im using the Doctrine Query Builder, and have a very specific requirement that came through.
I am using the ManyToMany field in my entity, related to User entity association (Array of User account entities).
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="User", cascade={"persist"})
* #ORM\JoinTable(name="post_user_list")
*/
protected $userList;
Amongst the requirements of displaying "public posts" requires that the Entity have a published boolean set to true, a published date less than the current date, and two users associated with entity.
In my query builder, I have setup this:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select($select)->from($this->getEntityName(), 'p');
$criteria = $qb->expr()->andX();
$criteria->add($qb->expr()->eq('p.editor_published', 1))
->add($qb->expr()->lte('p.datePublished', ':now'));
and that only handles the first two requirements, now I need a criteria entry for counting the amount of user entities in userList, and the where clause specifically for greater than or equal to two users.
Not exactly sure where to proceed..
Try this. The query uses HAVING to only display entities that are associated with 2 or more users.
$qb->select($select)
->from($this->getEntityName(), 'p')
->innerJoin('p.userList','u')
->where('p.editor_published = 1')
->andWhere('p.datePublished <= :now')
->groupBy($select) //not sure what's in $select may need to change this
->having('count(u.id) > 1'); //assuming user has an id column otherwise change it
->setParameter('now',new \DateTime());

Doctrine one-to-many situation: how to easily fetch related entities

To simplify, two entities are defined: User and Comment. User can post many comments and every comment has only one user assigned, thus Comment entity has:
/**
* #var \Frontuser
*
* #ORM\ManyToOne(targetEntity="Frontuser")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ownerUserID", referencedColumnName="id")
* })
*/
private $owneruserid;
However, when in action:
$orm = $this->getDoctrine()->getManager();
$repo = $orm->getRepository('CompDBBundle:Comment');
$repo->findBy(array('owneruserid' => $uid);
Error occured, that there's no such field like owneruserid.
How can I fetch all the user's comments then? The same happens to similar relations in my DB - looks likes you cannot run find() with foreign keys as parameters. I believe a function $user->getComments() should be automatically generated/recognised by Doctrine to allow efficient, quick access to related entities.
The example's simple but, what if there are more entities related to my User in the same way? Do I have to declare repositories for each and try to fetch them by it's owneruserid foreign keys?
Using doctrine, when you define a related entity it's type is the entity class (in this case FrontUser). Therefore firstly your related entity variable name is misleading. It should be e.g.
private $ownerUser;
Then, in order to do a findBy on a related entity field you must supply an entity instance e.g.
$orm = $this->getDoctrine()->getManager();
$userRepo = $orm->getRepository('CompDBBundle:FrontUser');
$user = $userRepo->findById($uid);
$commentRepo = $orm->getRepository('CompDBBundle:Comment');
$userComments = $commentRepo->findByOwnerUser($user);
If you don't have or want to retrieve the user entity you could use a DQL query with the 'uid' as a parameter instead.

Resources