Using distinct Doctrine2 - symfony

I am developing an application in symfony2 and using doctrine2. I created a custom repository class that has one function:
<?php
namespace Anotatzailea\AnotatzaileaBundle\Repository;
use Doctrine\ORM\EntityRepository;
/**
* InterpretatzeaRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class InterpretatzeaRepository extends EntityRepository
{
public function getInterpDesberdinak($value)
{
$qb = $this->createQueryBuilder('c')
->select('DISTINCT c.attribute')
->where('c.fer = :Value')
->setParameter('Value', $value);
$Emaitza = $qb->getQuery()->getResult();
return $Emaitza;
}
}
What I want to get with this function is an array of all the "Interpretatzea" objects that have a distinct c.attribute and all have c.fer = value. Is the query correct? I would also want to know how to pass the value parameter to the repository function. Thanks

A cursory look at your repository method suggests it looks okay :) IIRC, I think the use of DISTINCT there is fine. If you do have problems you can always do a GROUP BY instead.
As for calling the repo method in a controller and passing a $value variable to it, that's pretty straightforward; for example:
// in your controller
$value = 'foo';
// get doctrine connection from DI, etc.
$em = $this->getDoctrine()
->getEntityManager();
// get the repository object for your
// entity and call your repository method
$result = $em->getRepository('AnotatzaileaAnotatzaileaBundle:Interpretatzea')
->getInterpDesberdinak($value);
// ... do something with your $result here
Note you use a concatenated version of your namespace and bundle, followed by a colon and the entity; e.g: AcmeTestBundle:User
Hope this helps :)

Related

Symfony 4 - Can't access to my entity collection

I've a Symfony 4 project with User entity and SoldeConges Entity.
An user has a SoldeConges collection.
But when I dump the $user->getSoldeConges(), the collection is empty.
My User entity :
/**
* #ORM\OneToMany(targetEntity="App\Entity\SoldeConges", mappedBy="user", orphanRemoval=true)
*/
private $soldeConges;
/**
* #return Collection|SoldeConges[]
*/
public function getSoldeConges(): Collection
{
return $this->soldeConges;
}
And my user has 3 soldeConges :
PhpMyAdmin SoldeConge table :
And when I make a dump in my controller for my User (which is the user number 1) :
$soldeConges = $this->getUser()->getSoldeConges();
dump($soldeConges);
I've :
So, why can not access to my User SoldeConges collection ?
1)To get your soldeConges (this is symfony 3 code, adapt it to 4 ;-) ):
$em = $this->getDoctrine()->getManager();
$soldeCongesRepository= $em->getRepository('AppSoldeConges:SoldeConges');
$soldeConges = $soldeCongeRepository->findBy(['userId'=>$this->getUser()->getId()]);
2)It may be due to Doctrine lazy loading.
Try fetch="EAGER" (it's LAZY by default):
* #ORM\OneToMany(targetEntity="App\Entity\SoldeConges", mappedBy="user", orphanRemoval=true, fetch="EAGER")
Doctrine loads the whole collection in once IF you try to access it. A dump is the memory model at the moment where you place your dump() statement.
If you should render the collection first (or even only if you use the count() method on the collection) and then use the dump() statement you will see that your collection has been loaded. This is the system called lazy loading. It will execute a second query when needed. But as you may know if two queries could get one query then it should be better and faster.
On the other hand if you have entities with large collections this could get a serious problem. In that case you could use "extra lazy loading". (See the docs)
Anyway if you want to get your collections loaded immediately with your entities then you could use your own DQL query that have one or more JOINS. Below an example of your Repository with a new function called findAllWithJoin. Call that function from your controller instead of findAll().
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
public function findAllWithJoin()
{
$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT u, sc FROM User u JOIN u.soldeConges sc');
return $query->execute();
}
}

Single position to restrict access to a Doctrine entity

I've just started working with Doctrine and built a simple blog project. One of my requirements is that a blog post should not be visible to anybody (for simpleness, skip an editor's interface) until the publish date is reached.
As far as I see, it's obvious to do so using a custom repository. Let's extend the find method the following way:
public function find($id, $lockMode = null, $lockVersion = null)
{
/** #var Post $post */
$post = parent::find($id, $lockMode, $lockVersion);
if($post->getCreatedAt() > new \DateTime()) {
return null;
}
return $post;
}
This restricts the access for a page showing a single Post entity. For an overview page, the same can be done using a custom method:
public function findForOverview()
{
$query = $this->createQueryBuilder('p')
->where('p.createdAt < CURRENT_TIMESTAMP()')
->orderBy('p.createdAt', 'DESC')
->getQuery();
return $query->getResult();
}
So, even for this simple requirement, I've already written two custom methods. If I continue to work on my project, other restriction limitations might occur and additional ways to load that entity might arise. And as far as I see, for each case I have to implement the logic for all access guards.
Is there no simpler way to do that? I'm thinking of something like an annotation or an "entity load listener" that makes it simple to write one single entry point for all such checks - making it impossible to forget such checks...
Such restrictions are usually implemented by using mechanism of SQL filters in Doctrine. Implementation of this filter works on lower level then DQL and allows you to apply modifications for SQL query being constructed. In your case it may look like this:
namespace App\ORM\Filter;
use App\Entity\Post;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class PostVisibilityFilter extends SQLFilter
{
/**
* Gets the SQL query part to add to a query.
*
* #param ClassMetadata $targetEntity
* #param string $targetTableAlias
* #return string The constraint SQL if there is available, empty string otherwise
*/
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
{
if ($targetEntity->name !== Post::class) {
return '';
}
return sprintf('%s.%s >= now()', $targetTableAlias, $targetEntity->getColumnName('createdAt'));
}
}

Symfony2 doesn't seem to recognize my repository class

I am encountering the error Undefined method 'findAllCtrs'. The method name must start with either findBy or findOneBy!
I've tried all the other solutions on StackOverflow regarding this problem. I've cleared cache, cleared meta data cache, checked the namespaces and folder entities but still no fix.
Here is my entity:
namespace CFS\Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Ref
*
* #ORM\Table(name="ref", indexes={#ORM\Index(name="refno", columns={"refno"}), #ORM\Index(name="ctrno", columns={"ctrno"})})
* #ORM\Entity(repositoryClass="CFS\Bundle\Entity\RefRepository")
*/
class Ref
{
My repository class:
namespace CFS\Bundle\Entity;
use Doctrine\ORM\EntityRepository;
class RefRepository extends EntityRepository
{
public function findAllCtrs()
{
$query = $this->getEntityManager()
->createQuery('
SELECT
r.refno, r.ctrno
FROM
CFSBundle:Ref r
ORDER BY
r.refno DESC
');
try {
return $query->getResult();
} catch(\Doctrine\ORM\NoResultException $e) {
return null;
}
}
}
And I tried calling the method in my controller with:
$em = $this->getDoctrine()->getManager();
$containers = $em->getRepository('CFSBundle:Ref')
->findAllCtrs();
I did noticed that when I generate entities in the command line php app/console doctrine:generate:entities CFSBundle it is not recognizing my RefRepository.php file. What else could I have missed?
I stuck with a similar error too, after spending a day searching for a solution I found a typo in my annotation referencing the repository class.
But I don't see a mistake in the code you provided, hence it should work…
Did you read https://stackoverflow.com/a/15184084/1781752 ?
There seemed to be problems mixing yml mappings and annotations.

Knplabs translatable: How to find an entry by a translatable field?

I have an entity with the Knp Doctrine behaviors Translatable and Sluggable. Everything works fine so when I create or update an entry there is a slug for each language generated from the title.
The question ist how can i find an entry by a translated slug?
This will throw an error "has no field 'slug'":
$this->getDoctrine()->getRepository('AcmeTestBundle:News')->findOneBySlug($slug);
Unless someone has a smarter way of doing this then you will need to create a custom repository method that performs a join to the entity translations table to perform your lookup:
use Doctrine\ORM\EntityRepository;
/**
* NewsRepository
*/
class NewsRepository extends EntityRepository
{
public function findOneBySlug($slug)
{
$qb = $this->createQueryBuilder('n')
->select('n, t')
->join('n.translations', 't')
->where('t.slug = :slug')
->setParameter('slug', $slug);
return $qb->getQuery()->getSingleResult();
}
}
I have found that this method works, although I am still curious if there is a more generic solution to this
Try This:
$this->getDoctrine()
->getRepository('AcmeTestBundle:News')
->findOneBy(array("slug"=>$slug));
Hope this helps.

When to use Entity Manager in Symfony2

At the moment I am learning how to use Symfony2. I got to the point where they explain how to use Doctrine.
In the examples given they sometimes use the entity manager:
$em = $this->getDoctrine()->getEntityManager();
$products = $em->getRepository('AcmeStoreBundle:Product')
->findAllOrderedByName();
and in other examples the entity manager is not used:
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
So I actually tried the first example without getting the entity manager:
$repository = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product');
$products = $repository->findAllOrderedByName();
and got the same results.
So when do i actually need the entity manager and when is it OK to just go for the repository at once?
Looking at Controller getDoctrine() equals to $this->get('doctrine'), an instance of Symfony\Bundle\DoctrineBundle\Registry. Registry provides:
getEntityManager() returning Doctrine\ORM\EntityManager, which in turn provides getRepository()
getRepository() returning Doctrine\ORM\EntityRepository
Thus, $this->getDoctrine()->getRepository() equals $this->getDoctrine()->getEntityManager()->getRepository().
Entity manager is useful when you want to persist or remove an entity:
$em = $this->getDoctrine()->getEntityManager();
$em->persist($myEntity);
$em->flush();
If you are just fetching data, you can get only the repository:
$repository = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product');
$product = $repository->find(1);
Or better, if you are using custom repositories, wrap getRepository() in a controller function as you can get auto-completition feature from your IDE:
/**
* #return \Acme\HelloBundle\Repository\ProductRepository
*/
protected function getProductRepository()
{
return $this->getDoctrine()->getRepository('AcmeHelloBundle:Product');
}
I think that the getDoctrine()->getRepository() is simply a shortcut to getDoctrine()->getEntityManager()->getRepository(). Did not check the source code, but sounds rather reasonable to me.
If you plan to do multiple operations with the entity manager (like get a repository, persist an entity, flush, etc), then get the entity manager first and store it in a variable. Otherwise, you can get the repository from the entity manager and call whatever method you want on the repository class all in one line. Both ways will work. It's just a matter of coding style and your needs.

Resources