How to use a findBy method with comparative criteria - php

I'd need to use a "magic finder" findBy method using comparative criteria (not only exact criteria). In other words, I need to do something like this:
$result = $purchases_repository->findBy(array("prize" => ">200"));
so that I'd get all purchases where the prize is above 200.

The class Doctrine\ORM\EntityRepository implements Doctrine\Common\Collections\Selectable API.
The Selectable interface is very flexible and quite new, but it will allow you to handle comparisons and more complex criteria easily on both repositories and single collections of items, regardless if in ORM or ODM or completely separate problems.
This would be a comparison criteria as you just requested as in Doctrine ORM 2.3.2:
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->where(\Doctrine\Common\Collections\Criteria::expr()->gt('prize', 200));
$result = $entityRepository->matching($criteria);
The major advantage in this API is that you are implementing some sort of strategy pattern here, and it works with repositories, collections, lazy collections and everywhere the Selectable API is implemented.
This allows you to get rid of dozens of special methods you wrote for your repositories (like findOneBySomethingWithParticularRule), and instead focus on writing your own criteria classes, each representing one of these particular filters.

This is an example using the Expr() Class - I needed this too some days ago and it took me some time to find out what is the exact syntax and way of usage:
/**
* fetches Products that are more expansive than the given price
*
* #param int $price
* #return array
*/
public function findProductsExpensiveThan($price)
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$q = $qb->select(array('p'))
->from('YourProductBundle:Product', 'p')
->where(
$qb->expr()->gt('p.price', $price)
)
->orderBy('p.price', 'DESC')
->getQuery();
return $q->getResult();
}

You have to use either DQL or the QueryBuilder. E.g. in your Purchase-EntityRepository you could do something like this:
$q = $this->createQueryBuilder('p')
->where('p.prize > :purchasePrize')
->setParameter('purchasePrize', 200)
->getQuery();
$q->getResult();
For even more complex scenarios take a look at the Expr() class.

$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->where($criteria->expr()->gt('id', 'id'))
->setMaxResults(1)
->orderBy(array("id" => $criteria::DESC));
$results = $articlesRepo->matching($criteria);

The Symfony documentation now explicitly shows how to do this:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AppBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult();
From http://symfony.com/doc/2.8/book/doctrine.html#querying-for-objects-with-dql

I like to use such static methods:
$result = $purchases_repository->matching(
Criteria::create()->where(
Criteria::expr()->gt('prize', 200)
)
);
Of course, you can push logic when it is 1 condition, but when you have more conditions it is better to divide it into fragments, configure and pass it to the method:
$expr = Criteria::expr();
$criteria = Criteria::create();
$criteria->where($expr->gt('prize', 200));
$criteria->orderBy(['prize' => Criteria::DESC]);
$result = $purchases_repository->matching($criteria);

Copying the findBy query and modifying it to return your expected result is a good approach.

Related

Doctrine 2 simple "bigger than" criteria

In PDO / SQL i have a simple query with "bigger than":
DELETE * FROM tablexyz WHERE access > :old
Whats the equivalent in Doctrine 2 ORM?
I have already written a little code:
$criteria = ['access'=>$old];
$dataRepo = $entityManager->getRepository('tablexyz')->findBy($criteria)->delete();
My problem is, i want to use the bigger than operator in Doctrine 2 ORM but i don't want to make a function or class for that. Does somebody know a better or shorter solution for that?
You can use either a query builder og just do a direct DQL (or SQL) query for it.
$qb = $entityManager->createQueryBuilder();
$qb->select('e')
->from('Entityxyz', 'e')
->where('e.access > :old')
->setParameter('old', $old);
$entities = $qb->getQuery()->getResult();
or
$query = 'SELECT e FROM AppBundle\Entity\Entityxyz WHERE e.access > :old';
$entities = $entityManager->createQuery($query)
->setParameter('old', $old)
->getResult();`
I'd suggest you do create a repository method for it though, it's the correct place to have it. Using the query builder inside the repository is also simpler as it knows which entity you're referencing (and can skip calling select and from)
class EntityxyzRepository extends EntityRepository
{
public function getNewerThan($newerThan)
{
$qb = $this->createQueryBuilder('e');
$qb->where('e.access > :newerThan')
->setParameter('newerThan', $newerThan);
return $qb->getQuery()->getResult();
}
}

CodeIgniter to Symfony2 with Doctrine2

I'm really having a hard time converting this query to Doctrine:
public function get_order($id)
{
$this->db->select('*');
$this->db->from('tbl_orderline');
$this->db->join('tbl_order', 'tbl_order.orderNo = tbl_orderline.orderNo');
$this->db->join('tbl_customer', 'tbl_customer.customerNo = tbl_order.customerNo');
$this->db->join('tbl_product', 'tbl_product.productNo = tbl_orderline.productNo');
$this->db->where('tbl_order.orderNo', $id);
$query = $this->db->get();
return $query->result_array();
}
Could you please help me with this?
Any suggestions? Thanks
// if you're currently in custom Repository class then:
$qb = $this->createQueryBuilder('ol');
// if you're in a controller, then should be:
$qb = $em->getRepository('AppBundle:OrderLine')->createQueryBuilder('ol'); // Or whatever your bundle name is.
// query alias legend:
// ol - order line
// o - order
// c - customer
// p - product
// Your query builder should look something like this:
$qb
->addSelect('o, c, p')
->leftJoin('ol.order', 'o') // this is your relation with [order]
->leftJoin('o.customer', 'c') // this is your relation with [customer] from [order]
->leftJoin('ol.product', 'p') // this is your relation with [product] from [order line]
->where($qb->expr()->eq('ol.id', ':orderLineId')
->setParameter('orderLineId', $id)
->getQuery()
->getOneOrNullResult();
Note:
Since you did not provide any entity mappings, this is completely out of the blue. You are most likely going to change the properties in this query, but at the very least, it should give you the start you need.
Don't hesitate to ask, if you don't understand something.
Ive always found it easier to write in straight dql. Trying to use the querybuilder for complex stuff drives me nuts. This obviously requires you to have the correct relationships mapped in your entities, either annotated or with an orm file.
Obviously its hard to test what Ive put below, so you may need to debug a little.
$query = $this->getEntityManager()->createQuery(
'select orderline, order, customer, product
from BundleName:tlb_orderline orderline
join orderline.orderNo order
join order.customerNo customer
join orderline.productNo product
where order.orderNo = :id');
$query->setParameter('id' => $id);
return $query->getResult();

How to print single value of entity without dumping the whole object?

I want to get one single value from entity.Can anyone help me here.
Here is my code.Please let me know what is missing here.
$query = $em->createQuery("SELECT e FROM AdminBundle:MailTemplates e WHERE e.keyword = '" .$keywordVal."'");
$query->execute();
$result = $query->getResult();
echo $result ->getId();
Here i want the 'id'.
This is noted in the documentation how you can do this.
So given you're code this will become:
$query = $em->createQuery("SELECT e.id FROM AdminBundle:MailTemplates e WHERE e.keyword = ?1");
$query->setParameter(1, $keywordVal);
$query->execute();
$result = $query->getResult(); // array of MailTemplates ids
Note: I also made use of setParameters instead of setting the value directly in the query.
In your controller:
$this->get('database_connection')->fetchColumn('select id from mail_templates where...');
That's much better for performance and much easier if you don't want to have a deal with query builder and other doctrine orm stuff.
Using the query builder you could do...
$queryBuilder = $em->createQueryBuilder('e');
$queryBuilder
->select('e.yourColumn')
// This will return just this column
// Alternatively you could omit any select to return the whole object
// that you could then use like $object->getYourColumn() if you so chose
->where($queryBuilder->expr()->eq('e.keyword', ':keyword'))
->setParameter('keyword', $keyword)
;
return $queryBuilder
->getQuery()
->getResult();
try this on loading Entities instead of creating own queries
Loading the entity with the Repository.
$rep = $this->getDoctrine()->getManager()->getRepository("Bundlename:Entity");
//find one by keyword -> single entity
$entity = $rep->findOneBy(array('keyword' => $keyword));
//find all by keyword - Array of entities
$result = $rep->findBy(array('keyword' => $keyword));

Symfony2: How to convert 'Doctrine->getRepository->find' from Entity to Array?

I want to return an \Symfony\Component\HttpFoundation\JsonResponse with the record information, but I need to pass it as array.
Currently I do:
$repository = $this->getDoctrine()->getRepository('XxxYyyZzzBundle:Products');
$product = $repositorio->findOneByBarCode($value);
But now $product is an Entity containing all what I want, but as Objects. How could I convert them to arrays?
I read somewhere that I need to use "Doctrine\ORM\Query::HYDRATE_ARRAY" but seems 'findOneBy' magic filter does not accept such parameter.
*
* #return object|null The entity instance or NULL if the entity can not be found.
*/
public function findOneBy(array $criteria, array $orderBy = null)
Well thanks to dbrumann I got it working, just want to add it the full working example.
Also seems that ->from() requires 2 parameters.
$em = $this->getDoctrine()->getManager();
$query = $em->createQueryBuilder()
->select('p')
->from('XxxYyyZzzBundle:Products','p')
->where('p.BarCode = :barcode')
->setParameter('barcode', $value)
->getQuery()
;
$data = $query->getSingleResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
When you want to change the hydration-mode I recommend using QueryBuilder:
$query = $em->createQueryBuilder()
->select('p')
->from('Products', 'p')
->where('p.BarCode = :barcode')
->setParameter('barcode', $valur)
->getQuery()
;
$data = $query->getSingleResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
But what would probably be better, is adding a toArray()- or toJson()-method to your model/entity. This way you don't need additional code for retrieving your entities and you can change your model's data before passing it as JSON, e.g. formatting dates or omitting unnecessary properties.
Have done this two ways if you are working with a single entity:
$hydrator = new \DoctrineModule\Stdlib\Hydrator\DoctrineObject($entityManager);
$entityArray = $hydrator->extract($entity);
Last resort, you can just cast to an array like any other PHP object via:
$array = (array) $entity
NB: This may have unexpected results as you are may be working with doctrine proxy classes, which may also change in later Doctrine versions..
Can also be used:
$query = $em->createQueryBuilder()
->select('p')
->from('Products', 'p')
->where('p.BarCode = :barcode')
->setParameter('barcode', $valur)
->getQuery()
;
$data = $query->getArrayResult();

How can I query raw via Eloquent?

I am trying to do a query in my Laravel app and I want to use a normal structure for my query. This class either does use Eloquent so I need to find something to do a query totally raw.
Might be something like Model::query($query);. Only that doesn't work.
You may try this:
// query can't be select * from table where
Model::select(DB::raw('query'))->get();
An Example:
Model::select(DB::raw('query'))
->whereNull('deleted_at')
->orderBy('id')
->get();
Also, you may use something like this (Using Query Builder):
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
Also, you may try something like this (Using Query Builder):
$users = DB::select('select * from users where id = ?', array(1));
$users = DB::select( DB::raw("select * from users where username = :username"), array('username' => Input::get("username")));
Check more about Raw-Expressions on Laravel website.
You can use hydrate() function to convert your array to the Eloquent models, which Laravel itself internally uses to convert the query results to the models. It's not mentioned in the docs as far as I know.
Below code is equviolent to $userModels = User::where('id', '>', $userId)->get();:
$userData = DB::select('SELECT * FROM users WHERE id > ?', [$userId]);
$userModels = User::hydrate($userData);
hydrate() function is defined in \Illuminate\Database\Eloquent\Builder as:
/**
* Create a collection of models from plain arrays.
*
* #param array $items
* #return \Illuminate\Database\Eloquent\Collection
*/
public function hydrate(array $items) {}
use DB::statement('your raw query here'). Hope this helps.
I don't think you can by default. I've extended Eloquent and added the following method.
/**
* Creates models from the raw results (it does not check the fillable attributes and so on)
* #param array $rawResult
* #return Collection
*/
public static function modelsFromRawResults($rawResult = [])
{
$objects = [];
foreach($rawResult as $result)
{
$object = new static();
$object->setRawAttributes((array)$result, true);
$objects[] = $object;
}
return new Collection($objects);
}
You can then do something like this:
class User extends Elegant { // Elegant is my extension of Eloquent
public static function getWithSuperFancyQuery()
{
$result = DB::raw('super fancy query here, make sure you have the correct columns');
return static::modelsFromRawResults($result);
}
}
Old question, already answered, I know.
However, nobody seems to mention the Expression class.
Granted, this might not fix your problem because your question leaves it ambiguous as to where in the SQL the Raw condition needs to be included (is it in the SELECT statement or in the WHERE statement?). However, this piece of information you might find useful regardless.
Include the following class in your Model file:
use Illuminate\Database\Query\Expression;
Then inside the Model class define a new variable
protected $select_cols = [
'id', 'name', 'foo', 'bar',
Expression ('(select count(1) from sub_table where sub_table.x = top_table.x) as my_raw_col'), 'blah'
]
And add a scope:
public function scopeMyFind ($builder, $id) {
return parent::find ($id, $this->select_cols);
}
Then from your controller or logic-file, you simply call:
$rec = MyModel::myFind(1);
dd ($rec->id, $rec->blah, $rec->my_raw_col);
Happy days.
(Works in Laravel framework 5.5)
use Eloquent Model related to the query you're working on.
and do something like this:
$contactus = ContactUS::select('*')
->whereRaw('id IN (SELECT min(id) FROM users GROUP BY email)')
->orderByDesc('created_at')
->get();
You could shorten your result handling by writing
$objects = new Collection(array_map(function($entry) {
return (new static())->setRawAttributes((array) $entry, true);
}, $result));
if you want to select info it is DB::select(Statement goes here) just remember that some queries wont work unless you go to Config/Database.php and set connections = mysql make sure 'strict' = false
Just know that it can cause some security concerns
if ever you might also need this.
orderByRaw() function for your order by.
Like
WodSection::orderBy('score_type')
->orderByRaw('FIELD(score_type,"score_type") DESC')
->get();

Categories