In Symfony 5, using Doctrine, how can I get QueryBuilder object (instead of results) from default entity repository methods like findBy, findOneBy, findAll?
I need QueryBuilder for:
Passing it to KnpPaginator (requires specifically QueryBuilder instead of results)
Possibly extending it with additional query logic in the future
I could just write a simple query (like $em->createQuery("SELECT a FROM Article a")), but I want to have access to filtering and ordering provided by default findBy method. I think writing my own QueryBuilder with filtering/sorting by any property would be a lot of work and I'm not sure I could implement it well even if I tried.
EDIT: even though it does not answer my question exactly, I have found a solution for my 1st use case (using KnpPaginator without writing custom queries): Custom data repository pagination.
This method allows to attach pagination to any query without changing it instead of writing a new one through QueryBuilder.
Related
The tutorial of zend framework show that I can use Zend\Db\ResultSet\HydratingResultSet to return a model object using the dbAdapter ,select query and a model prototype. But in most case writing the website code I use multi-table query and the hydrator can not fix this problem but just a single table query. How should I deal with this kind of problem.
hydrator doc
And this problem can also happen when encounting the pagination which also only take one prototype.
paginator doc
Depending on the query, you might need a custom ResultSet that knows how to deal with the multi-row potential of a multi table query, and mapping that dataset appropriately.
Additionally, if you find yourself getting into very advanced object relationships with Zend\Db, that might be the time to start considering Doctrine2 (or a similar ORM).
So, I want to understand how the Doctrine Repository mechanism works.
For my entities I use annotations, so the resulting object is built somewhere during the execution of the script.
I'd like to unserstand which are the possibile ways of implementing the lazy loading of entities from another entity.
In concrete, using Doctrine, I have the ability to fetch information of related object (from the Symfony book). This fetching is done in a lazy way: only if I call the method to get the information about the Entity it is loaded from the database querying it.
Now, I'd like to better understand this mechanism: how an entity can implement repository methods?
How can I reproduce this mechanism to implement it in other context similar to the one of a database data retrieval?
As the resulting object is really big, is there someone who can put me on the right way?
Which classes should have I read to understand the mechanism?
Are there any articles/posts that better explain how this mechanism is implemented?
Are there better (or simply simpler) ways of implementing it?
I think the best description of the lazy loading can be found in Doctrine developer articles.
http://www.giorgiosironi.com/2009/07/lazy-loading-of-objects-from-database.html
http://www.giorgiosironi.com/2009/08/doctrine-2-now-has-lazy-loading.html
The main idea is to insert into Product's category list a set of objectes that are subclasses of Category. These and called "proxy objects" and created "on the fly" when Product is retrieved from database. These proxy objects have the same interface as Category object, but add functionality of loading actual Category items from database when needed.
Doctrine actually creates a extra object (think proxy) that keeps a record of what properties have actually been accessed.
See this part from the documentation :
32.4.2. Association proxies
The second most important situation where Doctrine uses proxy objects is when querying for objects. Whenever you query for an object that has a single-valued association to another object that is configured LAZY, without joining that association in the same query, Doctrine puts proxy objects in place where normally the associated object would be. Just like other proxies it will transparently initialize itself on first access.
doctrine documentation
You can create a custom repository for an entity: http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
Once you have your own custom repository class you can create queries that fetch all of the information you need in one query rather than relying on lazy loading.
So say you have an Product entity which has one or more Category entities using a ManyToMany relationship you could create a function in your custom repository to fetch all products with their categories in one query:
public function fetchProductsWithCategories()
{
return $this->getEntityManager()
->createQuery(
'SELECT p, c FROM Product p join p.categories c'
)
->getResult();
}
Then in your controller you would have something like:
$repo = $this->getDoctrine()->getManager()->getRepository('Product');
$products = $repo->fetchProductsWithCategories();
Edit: missed c in select
Okay, so here's the deal. I'm working with a custom CMS, and I'd like for the code to be as optimized as possible. I've been reading/watching tuts/etc. like crazy about the repository pattern in general as well as specifically using it with Laravel's Eloquent. There are probably some really dumb questions/thoughts in here, but bear with me. :-) Sometimes there's no easy way to ask about terminology/best practices without looking silly.
As with many things, there are million ways I could "make it work"; my dilemma is essentially a matter of "best practice."
General Scenario/Question
Let's assume I am trying to get a Page for my CMS from the database. From what I can understand the typical way to set up the repository pattern with Eloquent is to have the following files:
Page.php -- the Eloquent Model
PageRepositoryInterface.php -- the "contract" for what should be in Page repo's
EloquentPageRepository.php -- the Page repository that can grab data via Eloquent
Easy enough. So I might use it this way. Assuming I have a getPageById method in EloquentPageRepository.php, I could just do something like this from my controller:
$page = $this->repo->getPageById();
Now my question arises: what type of data should getPageById() return? Some people recommend setting it up to return an Eloquent collection. Others say just a plain array or generic object.
Ideally I feel like my scenario would best lend itself to having EloquentPageRepository grab the data from Eloquent and actually return an instance of a custom Page class that I have. For example, something along the lines of this:
<?php namespace Acme\Repositories\EloquentPageRepository;
use Acme\...\PageObject as PageObject; // Better name than PageObject?
//...
class EloquentPageRepository implements PageRepositoryInterface {
// Omitting constructor, etc.
public function getPageById($id)
{
// Grab the row via Eloquent (obviously not stored in Page:: at
// this point. I'm just using it here for clarity and time's sake.
$page = Page::find($id);
// Now we have an Eloquent collection stored in $page, but I'd
// like to return the data inside an instance of my custom class.
$pageObj = new PageObject($page->title, $page->body);
return $pageObj;
}
}
To me, this seems good because it gives a consistent delivery format from repo to repo. It also allows me to perform some constructor logic on my pageObject. Finally, it allows me to have some custom methods on the PageObject (that are repository-agnostic).
It's similar to a collection, but I don't think it's exactly that. It's basically just an instance of a class that I'm immediately populating with my database info.
My questions, listed:
Is it considered bad practice to use a repo to stuff eloquent data into a specific object and return it?
I don't want to call my class "PageObject," because that's just lame. I'd way rather call it something like "PageCollection," except for the fact that it's not actually a collection. Is there actually a name for the way that I'm using this class? It's not a collection, it's a ...? I have no idea about this, I'm just searching for any input you have.
It whole depends on what you expect from the repository pattern. Are you using the repository pattern because in the future you're going to swith of data layer and needs a new repository. If you're using Eloquent as long as your cms live then you can return an eloquent object. If you want it very flexible then make a new page object(PageComposer as mentioned in the comments). This is one of the strengts of the repository pattern so I suggest you make a PageComposer class which you instantiate and return by the repository.
Normally you can call it Page because its a page and it ships some information of a page. But that name you've already give to the Eloquent model. You can consider changing the eloquent models name and call your return object Page.
Is there a simple way to somehow filter DQL statements to be able to use DQL conditions in url query parameter for instance in a REST API? Like users/?q="firstName='John' AND contacts IS EMPTY".
Something like sandboxing mode in twig. Maybe a custom query walker could be used but probably someone has solved it already?
This is what custom Repository classes are for. In your custom Repository class, you can create oneor more methods that accept parameters and use the querybuilder to build up your query depending on the url parameters passed. See the documentation for the QueryBuilder class for many examples.
So i created a custom AST Walker. Looks like it works. But it's not fully tested yet.
https://gist.github.com/maryo/6762610
I am attempting to encrypt certain database fields by adding a call to mysql AES_ENCRYPT (and AES_DECRYPT) using Doctrine DQL Hooks.
for SQL SELECT's I have a preDqlSelect() in my Doctrine_Record_Listener class, which goes through each parameter in the select fields, and adds a AES_DECRYPT() for encrypted fields.
But, it looks like calling save() on a doctrine model class only calls the preSave() listener and does not call any of the preDql* listeners.
Can anyone suggest a way of getting this to work or a better way of doing this?
Thanks
In order for these dql callbacks to be checked, you must explicitly turn them on. Because this adds a small amount of overhead for each query, it is off by default
$manager->setAttribute(Doctrine_Core::ATTR_USE_DQL_CALLBACKS, true);
Doctrine 1.2 Event listeners