I am working on a project using symfony2. I already have my search function and it is already working. But what I want is to have pagination on the search results since there are times that there are a lots of data being retrieve. How can I possibly do it?
Here is my code :
public function searchAction (Request $request) {
$request = $this->getRequest();
$data = $request->get('search');
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery (
'SELECT a,b,c FROM SampleBundle:transactDetail a
JOIN a.transact b
JOIN b.transactType c
WHERE b.pNumber LIKE :data
OR b.senderId LIKE :data'
)
->setParameter('data', "%$data%");
$result = $query->getResult();
if ($result == null ) {
return $this->render('SampleBundle:Sample:noresult.html.twig');
}
return $this->render('SampleBundle:Sample:search.html.twig', array('result' => $result));
}
Thanks in advance.
This is an example of your code with Paginator :
public function searchAction(Request $request, $firstResult = 0, $maxResults = 100)
{
$request = $this->getRequest();
$data = $request->get('search');
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT a,b,c FROM SampleBundle:transactDetail a
JOIN a.transact b
JOIN b.transactType c
WHERE b.pNumber LIKE :data
OR b.senderId LIKE :data'
)
->setParameter('data', "%$data%")
->setFirstResult($firstResult)
->setMaxResults($maxResults);
$result = new Paginator($query);
if ($result == null) {
return $this->render('SampleBundle:Sample:noresult.html.twig');
}
return $this->render('SampleBundle:Sample:search.html.twig', array('result' => $result));
}
And if you would like to count the total just count($result);.
Paginator has Countable et IteratorAggregate interfaces.
So you can loop on your result without problem.
Related
I am making some DB query via repository (ORM), I want to get a list of objects.
What's my problem?
I have two methods in the repository: getList() to get a list and getCount() to get the number of total records for information.
getList() gives me 5 records, and getCount() gives the number 9.
public function getList($criteria, $page, $limit, $orderBy, $joins = [])
{
$qb = $this->createQueryBuilder('a')->select('a');
if(!empty($joins)) {
$qb = $this->setCriteriaByJoins($qb, $joins);
}
$qb = $this->setCriteriaToQuery($qb, $criteria);
foreach($orderBy as $order) {
$qb->addOrderBy('a.' . $order[0], $order[1]);
}
if($page && $limit) {
$offset = ($page - 1) * $limit;
$qb->setFirstResult($offset)
->setMaxResults($limit);
}
$result = $qb->getQuery()->getResult();
return $result;
}
public function getCount($criteria = [], $joins = []) {
try {
$qb = $this->createQueryBuilder('a')->select('count(a.id)');
if(!empty($joins)) {
$qb = $this->setCriteriaByJoins($qb, $joins);
}
$qb = $this->setCriteriaToQuery($qb, $criteria);
$result = $qb->getQuery()->getSingleScalarResult();
} catch(\Exception $e) {
$result = 0;
}
return $result;
}
In debugging, I see that the getList() request was built like this:
SELECT r0 _. * FROM film r0_ LEFT JOIN person_film r1_ ON (r0_.id = r1_.film_id) WHERE r1_.person_id IN (45793) AND r0_.status = 1 ORDER BY r0_.`release` DESC, r0_.id DESC LIMIT 48
and getCount()
SELECT count (r0_.id) AS sclr_0 FROM film r0_ LEFT JOIN person_film r1_ ON (r0_.id = r1_.film_id) WHERE r1_.person_id IN (45793) AND r0_.status = 1
Requesting getList() from the repository gives me 5 records. However, if I directly ask the database with this query (without ORM), then I will get 9 records. Of these 9 records, 4 are duplicated due to the fact that the person_film table can contain more than one record with the same film_id and person_id (these records differ in other fields). I found that /vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php:hydrateRowData() removes duplicates.
How to do to make the ORM in the getList() request return all 9 records, even if they are duplicates?
And vice versa. How to do to the getCount() to return 5.
it seems like this is the answer to the second question
public function getCount($criteria = [], $joins = [], $distinct = false) {
try {
$sq = ($distinct) ? 'count(distinct(a.id))' : 'count(a.id)';
$qb = $this->createQueryBuilder('a')->select($sq);
if(!empty($joins)) {
$qb = $this->setCriteriaByJoins($qb, $joins);
}
$qb = $this->setCriteriaToQuery($qb, $criteria);
$result = $qb->getQuery()->getSingleScalarResult();
} catch(\Exception $e) {
$result = 0;
}
return $result;
}
I want to create a query using Doctrines QueryBuilder.
The User can choose the database fields which shall be searched by checking or unchecking checkboxes in an HTML form. That is why $_POST contains 'filters' and 'like'.
$_POST['filters'] looks like this:
array(2) { [0]=> string(4) "rack" [1]=> string(5) "shelf" }
I'm trying to get the following query work to result in
SELECT * FROM `inventories` WHERE (`rack` OR `shelf` LIKE '%01%') AND `checkedOutAt` IS NULL ORDER BY `lastChangedAt`
I'm using Doctrine in Version 2.5.5 and PHP in Version 7. My controller looks like this:
public function searchAction()
{
$filters = array();
$em = $this->getEntityManager();
$vendors = $em->getRepository('Entities\Vendor')->findAll();
if ($_POST)
{
$filters = $_POST['filters'];
$like = trim($_POST['like']);
$inventories = $em
->getRepository('Entities\Inventory')
->findInventoriesBySearch($like, $filters)
;
$this->addContext('like', $like);
}
else
{
$inventories = $em
->getRepository('Entities\Inventory')
->findInventories()
;
}
$count = count($inventories);
$this->addContext('filters', $filters);
$this->addContext('vendors', $vendors);
$this->addContext('inventories', $inventories);
$this->addContext('count', $count);
$this->setTemplate('inventoryAction');
}
And the according repository (the "findInventories()" repository function works fine):
public function findInventoriesBySearch($like, $filters)
{
$em = $this->getEntityManager();
$orExpr = $qb->expr()->orX();
foreach ($filters as $filter)
{
$orExpr->add($qb->expr()->like($filter, $like));
}
$qb ->andWhere('i.checkedOutAt is NULL');
$qb->setParameter('like', '%' . $like . '%');
$qb->select('i')->where($orExpr)->addOrderBy('i.lastChangedAt', 'DESC');
return $qb->getQuery()->getResult();
}
When I run the script I receive the following error message:
Fatal error: Uncaught Doctrine\ORM\Query\QueryException: SELECT i
WHERE rack LIKE 01 OR shelf LIKE 01 ORDER BY i.lastChangedAt DESC in
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php:41
Stack trace: #0
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(483):
Doctrine\ORM\Query\QueryException::dqlError('SELECT i WHERE ...') #1
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(971):
Doctrine\ORM\Query\Parser->semanticalError('line 0, col 15 ...',
Array) #2
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(1702):
Doctrine\ORM\Query\Parser->AbstractSchemaName() #3
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(1557):
Doctrine\ORM\Query\Parser->RangeVariableDeclaration() #4
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php(1292):
Doctrine\ORM\Query\Parser->IdentificationVariableDeclaration() in
.../vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php on
line 63
Try check up & change it.
(Just, an advice) Don't work with $_POST/ request type/ access to it/etc. via "your way". Use typehinted Request e.g. :
public function searchAction(Request $request){
if ($request->isMethod('POST')) {
$filters = $request->request->get('filters');
//.....
}else{
}
}
Guess, the problem there is:
//...
foreach ($filters as $filter)
{
$filter = 'i.'.$filter; // <== 'i.rack','i.shelf', so on
$orExpr->add($qb->expr()->like($filter, $like));
}
I have the following function:
public function getUserMediaAction(Request $request)
{
$data = $this->decodeToken($request);
$username = $data['email'];
$user = $this->getDoctrine()->getRepository('ApplicationSonataUserBundle:User')
->findOneBy(['email' => $username]);
$idUser = $user->getId();
$media = $this->getDoctrine()->getRepository('ApplicationSonataUserBundle:User')
->findNewbieAllData($idUser);
return new JsonResponse(['media' => $media->getPath()]);
}
findNewbieAllData:
public function findNewbieAllData($id_newbie)
{
$sql = "SELECT m
FROM ApplicationSonataUserBundle:User u
JOIN ApplicationSonataUserBundle:Media m
WHERE u.id=?1";
$query = $this->getEntityManager()
->createQuery($sql)
->setParameter(1, $id_newbie);
$result = $query->getArrayResult();
return $result;
}
which unfortunately returns the following error:
Call to a member function getPath() on array
Stack Trace
in src\CoolbirdBundle\Controller\UserController.php at line 97 -
$media = $this->getDoctrine()->getRepository('ApplicationSonataUserBundle:User')
->findNewbieAllData($idUser);
return new JsonResponse(['media' => $media->getPath()]);
}
Would anyone have any idea of how I can solve this problem, please?
thank you in advance
Try the following instead:
return new JsonResponse(['media' => $media[0]->getPath()]);
Edit: Looking at the var_dump output you should try this:
return new JsonResponse(['media' => $media[0]['path']]);
Method findNewbieAllData hydrates data with arrays.
$result = $query->getArrayResult();
return $result;
Look at getArrayResult method here:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/AbstractQuery.php
Use getResult method instead.
I have created the following for a product catelog/lister:
public function index($type_id = null) {
$filters = $sort = array();
if (isset($type_id)) {
$filters['type'] = $type_id;
} else {
$filters['type'] = Input::get('type');
}
$filters['search'] = Input::get('search');
$filters['brand'] = Input::get('brand');
$sort['sort'] = Input::get('sort');
$sort['sortdir'] = Input::get('dir');
$productsPaginated = $this->fetchProducts($filters, $sort);
return View::make('products.products', array(
'productsList' => $productsPaginated
)
);
}
public function fetchProducts($filters, $sorts, $perpage = 2) {
print_r($filters);
$Product = Product::query();
if (!empty($filters['search']))
$Product->where('name', 'LIKE', '%' . $filters['search'] . '%');
if (isset($filters['type']))
$Product->where('type_id', $filters['type']);
if (isset($filters['brand']))
$Product->where('brand_id', $filters['brand']);
if (isset($sorts['sort']))
$Product->orderBy($sorts['sort'], $sorts['sortdir']);
$Product = $Product->paginate($perpage);
return $Product;
}
Which works well so far.
I am now trying to create some filters so a user can further filter the results.
How can I access and determine distinct rows based on a column in:
$productsPaginated = $this->fetchProducts($filters, $sort);
?
The groupBy method not only exists on the query builder but also on the collection class. (which will be returned when calling paginate)
Take a look at the source on github
So add an argument to your function and use groupBy
public function fetchProducts($filters, $sorts, $perpage = 2, $groupBy = null) {
// code omitted for brevity
$Product = $Product->paginate($perpage);
if($groupBy){
$Product = $Product->groupBy($groupBy);
}
return $Product;
}
Update
Then there's the lists function that works on collections as well as on query builders...
$Product->lists('column-name');
Update 2
I was curious so I did some testing and a found something very weird and I have no idea if its a bug or a feature I don't understand
When calling groupBy the collection returned has actually only one item (index "") and this item contains an array of the "original" items. So to make lists work. I found this workaround
$Product = $Product->groupBy($groupBy);
$Product = new Collection($Product[""]); // \Illuminate\Support\Collection
$Product = $Product->lists('column-name');
I have a native query an want to fill a ResultSetMapping object with the results of my query.
Everything works as expected, but the field "distance". The field in the resulting array is always empty. When i run my query directly in MySQL there are results for "distance", so i guess my SQL is ok?
class PlaceRepository extends EntityRepository
{
public function getAllWithMainImage($lat, $lon)
{
try
{
$rsm = new ResultSetMapping;
$rsm->addScalarResult('slug', 'slug');
$rsm->addScalarResult('title', 'title');
$rsm->addScalarResult('rating', 'rating');
$rsm->addScalarResult('visited_at', 'visited_at');
$rsm->addScalarResult('img', 'img');
$rsm->addScalarResult('distance', 'distance');
return $this->getEntityManager()
->createNativeQuery('SELECT p.slug, p.title, p.rating, p.visited_at, pp.file AS img, ROUND(GLength(
LineString(
p.place_point,
GeomFromText("POINT(:lat :lon)")
)
) * 111.12, 1) AS distance
FROM place p
LEFT JOIN place_picture AS pp
ON p.id = pp.place_id
GROUP BY p.id
ORDER BY distance
LIMIT 20', $rsm)
->setParameter('lat', $lat)
->setParameter('lon', $lon)
->getResult();
}
catch(\Doctrine\ORM\NoResultException $e)
{
return false;
}
}
}
Repository function called from Crontroller:
class PlaceRestController extends Controller
{
public function getPlacesAction(ParamFetcher $paramFetcher)
{
$view = FOSView::create();
$view->setHeader('Access-Control-Allow-Origin', '*');
$em = $this->getDoctrine()->getManager();
$data = $em->getRepository('TouringPlaceBundle:Place')->getAllWithMainImage(48.39227479328352, 9.985520839691162); //hard-coded for debugging
print_r($data); //… [img] => xzz.jpg, [distance] =>
if($data)
{
$view->setStatusCode(200)->setData($data);
}
return $view;
}
}
Where is my mistake?
Can you add the part of your code where you are calling this method?
Where does the return value go? In an entity?
Evenf if I don't think so, the problem might come from the multiples quotes so :lat and :lon are not changed into the values you want them to be. Have you checked the profiler to be sure they are replaced?