Use elastica with PagerFanta and dynamic querybuilder - symfony

I have an application with symfony2 / doctrine 2 / elastica / fosElasticaBundle / pagerFanta.
I want to use a custom and dynamic queryBuilder in combination with pagerfanta and elastica. Not to transform the results but to prefilter them.
So far I was able to :
1. Use pagerfanta by itself with my custom queryBuilder :
$page = $request->get('page', 1);
$search = $request->get('search');
$querybuilder = $this->getDoctrine()->getRepository('AppBundle:FoodAnalytics\Recipe')->findByTopCategoryQueryBuilder($category);
$explorerManager = $this->get('explorer_manager');
$pagerFanta = $explorerManager->getPagerFanta($querybuilder, $page, 4);
$recipes = $pagerFanta->getCurrentPageResults();
Use Elastica with Pagerfanta but without my custom QueryBuilder :
$page = $request->get('page', 1);
$search = $request->get('search');
$finder = $this->container->get('fos_elastica.finder.website.recipe');
$pagerFanta = $finder->findPaginated($search);
$recipes = $pagerFanta->getCurrentPageResults();
Now, how can I also use my custom QueryBuilder ? I know you can set a custom one in elastica config but mine has to be dynamic = take an argument, so I'd like to set it in the controller. Is that possible ?

You can pass custom Elastica\Query to findPaginated method:
$query = new \Elastica\Query::create(new \Elastica\Query\Term(array(
'name' => $request->get('search')
));
$finder = $this->container->get('fos_elastica.finder.website.recipe');
$pagerFanta = $finder->findPaginated($query);
$recipes = $pagerFanta->getCurrentPageResults();
Maybe code is more clear and you can see how it works when you use just PagerFanta with ElasticaAdapter without FOSElasticaBundle:
// Searchable can be any valid searchable Elastica object. For example a Type or Index
$finder = new \Elastica\Index($elasticaClient, 'website.recipe');
// A Query can be any valid Elastica query (json, array, Query object)
$query = new \Elastica\Query::create(new \Elastica\Query\Term(array(
'name' => $request->get('search')
));
$elasticaAdapter = new \Pagerfanta\Adapter\ElasticaAdapter($finder, $query);
$paginator = new \Pagerfanta\Pagerfanta($elasticaAdapter);
$results = $paginator
->setMaxPerPage($limit)
->setCurrentPage($page)
->getCurrentPageResults();

Related

how to use like with findBy in symfony3?

I am using symfony3:
I want use like in my query.
$repository = $this->getDoctrine()->getRepository('AppBundle:ABC');
echo $searchstring = "LIKE '%".$searchtxt."%'"; die;
$res=$repository->findBy(array('fname' => $searchstring));
$normalizer = new ObjectNormalizer();
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
$response=$serializer->serialize($res, 'json'); // Output: {"name":"foo","sportsman":false}
return new Response($response);
Its return null
Anyone can suggest me ?
The find*() methods of Doctrine repositories don't allow to perform queries using LIKE for comparison. You need to write a custom DQL query (see Querying for Objects with DQL and Doctrine Query Language):
$query = $em->createQuery("SELECT abc FROM AppBundle:ABC abc WHERE fname LIKE :fname");
$query->setParameter('fname', 'LIKE "%'.$searchtxt.'%"');
$result = $query->getResult();

Key "productTitle" for array with keys "0, catTitle" does not exist in EagleShopBundle:global:product.html.twig

I'm trying to join two tables and print a value in twig template but I'm having this issue.
This is my Controller action.
/**
* #Route("products/display/{id}")
* #Template()
*/
public function displayAction($id) {
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p, pc.catTitle')
->from('EagleShopBundle:Products', 'p')
->leftJoin('EagleShopBundle:ProductCategory', 'pc', \Doctrine\ORM\Query\Expr\Join::WITH, 'pc.id = p.category')
->where($qb->expr()->eq('p.id', '?5'))
->setParameter(5, $id);
$product = $qb->getQuery()->getOneOrNullResult();
return $this->render("EagleShopBundle:global:product.html.twig", array(
'product' => $product,
'image_path' => '/bundles/eagleshop/images/'
));
}
This is my twig file line related to the issue,
<h1>{{product.productTitle}}</h1>
I guess issue is related to this line
$qb->select('p, pc.catTitle')
This is the error I get,
Key "productTitle" for array with keys "0, catTitle" does not exist in
EagleShopBundle:global:product.html.twig
You could try next query:
$qb->select('p, partial pc.{id, catTitle}')
// if you need full productCategory object then write just 'p, pc'
->from('EagleShopBundle:Products', 'p')
->leftJoin('p.category', 'pc')
//productCategory is the field
//in product entity which has relation to product category entity,
//paste your field (not column!) name here
//if it is not productCategory
->where('p.id = :productId')
->setParameter('productId', $id);
P.S.
It is better to move queries to entity repositories :)
P.P.S.
Doctrine partial objects
UPD
Fixed query - with right field name

How Can I Get _rev field in CouchDB Doctrine. Check if the document has changed

I use Doctrine for my CocuhDb
I retrieve My Object by
$dm = $this->container->get('doctrine_couchdb.odm.default_document_manager');
$obj = $dm->getRepository("myEntity")->findOneBy(array('title' => 'foo));
I tried with (pseudo-code)
for {
.....
$dm->persist($obj);
$uow = $dm->getUnitOfWork();
$changeset = $uow->getDocumentRevision($obj);
}
$dm->flush()
but my $obj has the old _rev.
I want to check if my document change _rev.
How Can I retrieve that information ?

Symfony2 - Best practice to refactor code/methods?

I'm using a search code and pagination code in my controller(s), it goes without saying it's bad coding habits by repeating code. That being said what is the best practice in Symfony2, to avoid repeating code in all my controllers?
And how do I access the code once it's been re-factored?
Controller
// Search code
$results = null;
$query = $request->query->get('q');
if (!empty($query)) {
$em = $this->getDoctrine()->getManager();
$results = $em->createQueryBuilder()
->from('AcmeDemoBundle:Blog', 'b')
->select('b')
->where('b.title LIKE :search')
->setParameter(':search', "%${query}%")
->getQuery()
->getResult();
}
// Pagination code
$page = $request->get('page');
$count_per_page = 5;
$total_count = $this->getTotalBlogs();
$total_pages = ceil($total_count/$count_per_page);
if (!is_numeric($page)) {
$page = 1;
} else {
$page = floor($page);
}
if ($total_count <= $count_per_page) {
$page = 1;
}
if (($page * $count_per_page) > $total_count) {
$page = $total_pages;
}
$offset = 0;
if ($page > 1) {
$offset = $count_per_page * ($page - 1);
}
$em = $this->getDoctrine()->getManager();
$blogQuery = $em->createQueryBuilder()
->select('b')
->from('AcmeDemoBundle:Blog', 'b')
->addOrderBy('b.created', 'DESC')
->setFirstResult($offset)
->setMaxResults($count_per_page);
$blogFinalQuery = $blogQuery->getQuery();
$blogPage = $blogFinalQuery->getArrayResult();
foreach ($blogPage as $blog) {
$blog_id = $blog['id'];
$commentRepository = $this->getDoctrine()
->getRepository('AcmeDemoBundle:Comment');
$comments[] = $commentRepository->findByBlog($blog_id);
}
// exit(\Doctrine\Common\Util\Debug::dump($comments));
return $this->render('AcmeDemoBundlBundle:Default:index.html.twig', array(
'blogPage' => $blogPage,
'total_pages' => $total_pages,
'current_page' => $page,
'comments' => $comments,
'query' => $query,
'results' => $results,
));
For a start, you can put all your custom queries in custom repository classes. I suspect that would cover all the re-use you need in this case.
For example, create a BlogRepository class in AcmeDemoBundle:Repository and annotate the Blog entity class as follows to define it's repository class:
/**
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Repository\BlogRepository")
*/
Then add methods to the repository for each custom query that's needed, bearing in mind the way that repository methods are typically named. It looks as though the controller method getTotalBlogs() could also be a method on BogRepository e.g.:
public function findFiltered($page, $count_per_page = 5, $filterText = '')
{
$total_count = $this->findTotal();
// Code to initialise page and offset here
$queryBuilder = $this->createQueryBuilder('blog');
$queryBuilder->...
...
}
Note that the above method could be used to get all the blogs if no $filterText is passed in. You would just need something like this:
if (!empty($filterText))
{
queryBuilder->where('b.title LIKE :search')
->setParameter(':search', "%${query}%")
}
Then, a CommentRepository could be created with a method to find all the comments for a given set of blog(id)s. Note you could use a sql 'IN' clause to get all the comments using a single query:
$commentQuery = $em->createQueryBuilder()
->select('comment')
->from('AcmeDemoBundle:Comment', 'comment')
->where('comment.blog IN (:ids)')
->setParameter('ids', $blogIds);
In addition to custom repository classes I use manager services (e.g. BlogManager) to encapsulate business processes. My controllers mainly use the managers rather than using the repositories directly but it depends on the functionality.
I'm a little confused that you have an overall results query which only returns Blogs where the title is like the search text whilst the paged query returns (a page of) all blogs. That may just be a because your code is in progress?

Use Limit and Offset in Doctrine2 query

I'm trying to do the pagination, but there is an error:
[Syntax Error] line 0, col 57: Error: Expected end of string, got 'limit'
I'm not quite sure if this is the right syntax (and logic) to make my query:
public function getFriendsFromTo ($user, $limit, $offset)
{
return $this->getEntityManager()
->createQuery('SELECT f FROM EMMyFriendsBundle:Friend f WHERE f.user='.$user.' limit '.$limit. 'offset' .$offset)
->getResult();
}
Friends and users are related manyToOne and oneToMany, so in the friends table there is a field - user_id.
This is in my controller:
$user = $this->get('security.context')->getToken()->getUser();
$id = $user->getId();
$friends = $user->getFriends();
$result = count($friends)
$FR_PER_PAGE = 7;
$pages = $result/$FR_PER_PAGE;
$em = $this->getDoctrine()->getEntityManager();
$friends = $em->getRepository('EMMyFriendsBundle:Friend')
->getFriendsFromTo($id, $FR_PER_PAGE, $page*$FR_PER_PAGE);
I know that it's stupid and even wrong (especially the third parameter to be $page*$FR_PER_PAGE), but I just wanted to try if the query works, and it didn't.
Nope. Use:
return $this->getEntityManager()
->createQuery('...')
->setMaxResults(5)
->setFirstResult(10)
->getResult();
$towary = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Towar')
->findBy(array(),array(),10,($current-1)*$numItemsPerPage);
You can use findBy 3rd and 4th parameters of method of doctrine repository, which are limit and offset.
Here is the method definition:
findBy(
array $criteria,
array $orderBy = null,
integer|null $limit = null,
integer|null $offset = null
)
Source: http://www.doctrine-project.org/api/orm/2.2/class-Doctrine.ORM.EntityRepository.html
you can also use
$query->getSingleResult();
Doctrine2.6, stumbled upon this old post and tried the DQL way but it did not fit for purpose. So if you want to avoid using DQL because you already have Entities mapped and joined together, you can do paging using matching & Criteria
$criteria = Criteria::create()
->setMaxResults($limit ? $limit : null)
->setFirstResult($offset ? $offset : null)
$result = $em->getRepository('EMMyFriendsBundle:Friend')
->matching($criteria)->toArray();

Resources