Dynamically update repository queries - php

I've created a query in a repository.
I want to change some elements in this query (like where condition, sort, etc) thanks to parameters sent via the function.
This function works and returns data :
public function dashboardIndex($offset, $limit, $order)
{
$query = $this->createQueryBuilder('v')
->setFirstResult($offset)
->setMaxResults($limit)
->getQuery()
->getResult()
;
return $query;
}
Adding dynamic "addOrderBy" doesn't work and it doesn't return data
public function dashboardIndex($offset, $limit, $order)
{
$query = $this->createQueryBuilder('v');
/* Sort dynamically $order = ['column1' => 'ASC', 'column2' => DESC, ...] */
foreach($order as $column => $direction){
$query->addOrderBy('v.'.$column , $direction);
}
/* Sort dynamically */
$query->setFirstResult($offset)
->setMaxResults($limit)
->getQuery()
->getResult()
;
return $query;
}
"createQueryBuilder" returns an object, but cannot it be modified all along the function?

Obvious statement is obvious:
To return the result of a query, you have to return the result of the query.
hence return $query; doesn't return the result, but the query.
better:
return $query->getQuery()->getResult();

Related

Is it possible to combine two DQL queries from one table so that pagination works correctly?

There is a table with next fields: 'is_international', 'category', 'countries' and others, but they are not important.
I want to optimize a DQL query to get data:
/**
* #param bool $international
* #param int[]|null $category
* #param string|null $country
*
* #return Query
*/
public function getItem(
bool $international,
?array $category,
?string $country
): Query {
$qb = $this->createQueryBuilder('c')
->orderBy('c.createdAt', 'DESC')
;
if (empty($category) && !isset($country)) {
return $qb
->andWhere('c.isInternational = true')
->getQuery()
;
}
if (!empty($category)) {
$qb
->innerJoin('c.category', 'category')
->andWhere('c.category IN (:category) AND c.isInternational = false')
->setParameter('category', $category)
;
}
if (isset($country)) {
$qb
->innerJoin('c.countries', 'cc', 'WITH', 'cc.name = :country')
->setParameter('country', $country)
;
}
return $qb->getQuery();
}
There is a boolean value $international and its essence is that if it true and passed any of parameters $category or $country, I'm want to add international items from this table to query:
if ($international) {
$qb
->addSelect('int')
->join(Coupon::class, 'int')
->andWhere('int.isInternational = true')
;
}
Actually adding them is not difficult, but the difficulty is that Knp\Component\Pager\PaginatorInterface incorrectly displays limits and as far as I understand, this is due to the new addSelect('int') he gives the right items and all international items... how to get the right selection?
Actually, as always, the answer lay on the surface, it was only necessary to apply it correctly...
if (!empty($category)) {
$qb
->andWhere('c.category IN (:category) AND c.isInternational = false')
->setParameter('category', $category)
;
}
if (isset($country)) {
$qb
->leftJoin('c.countries', 'cc')
->andWhere('cc.name IN (:country)')
->setParameter('country', $country)
;
}
if ($international) {
$qb
->orWhere('c.isInternational = true')
->addOrderBy('c.isInternational', 'ASC')
;
}

couldn't get data using laravel eloquent

What I want is that when a user visits this link /api/bonus?provider[]=MyCompany
the result will show only bonus provided by providers=[MyCompany].
In my controller:
public function all(Request $request)
{
$size = $request->input('size');
$bonus = Bonus::with('category', 'bonus');
$bonus = Filterer::apply($request, $size, $bonus);
$bonus = Filterer::apply($request, $size, $bonus);
return response()->json([
'code' => 0,
'success' => true,
'data' => BonusResource::collection($bonus),
}
My expected result is to get all the providers that equal the [MyCompany]
But somehow this query doesn't work.
Filterer
public static function apply(Request $filters, $size, $bonus)
{
if ($filters->has('provider')) {
$bonus->whereHas('bonus', function ($query) use ($filters) {
$query->whereIn('providers', $filters->input('provider',[]));
});
}
return $bonus->paginate($size);
}
but at the end I'm getting this result. The data is null [].
I'm wonder why I can't get the data. Which part I had done wrong?
To actually get results you should use some function that gets data from database, for example get, first (depends whether you want to get multiple rows or only first row). Now you only prepare query that should be executed but you never execute it, so yo should change line:
$bonus = Filterer::apply($request, $size, $bonus);
into
$bonus = Filterer::apply($request, $size, $bonus);
$bonus = $bonus->get();
to execute query and get results.
I might be wrong, but based on your code, the function apply didn't return anything, it will always empty. Shouldn't it be return $bonus? and don't forget the get.
public static function apply(Request $filters, $size, $bonus)
{
if ($filters->has('provider')) {
$bonus->whereHas('bonusRelease', function ($bonus) use ($filters) {
return $bonus->whereJsonContains('providers', $filters->input('provider',[]));
});
}
return $bonus->get();
}
Updated my answer
The providers is a json type. You can't just query it with where in (a,b,c). Change to whereJsonContains. Please check this link another case and laravel docs.
First, convert the input into array, here I'm using explode()
and then I loop the converted array and use %% to search the providers. Hope it will help!
if ($filters->has('provider') && trim($filters->input('provider')) != "") {
$bonus_list->whereHas('bonusCompany', function ($query) use ($filters) {
$providers = explode(',', (trim($filters->input('provider'), '[]')));
foreach ($providers as $key => $provider) {
if ($key == 0) {
$query->where('providers', 'LIKE', ['%' . trim($provider) . '%']);
} else {
$query->orwhere('providers', 'LIKE', ['%' . trim($provider) . '%']);
}
}
});
};

skip parameter value of a function

Let suppose I have a function:
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 200,$type='result')
{
//Query generation according to above parameters
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}
This function is been used to many pages. Now, I want to skip the parameter $limit From function. That means I don't want to give $limit value but I need all data from database.
So what happens is if I have data like 600 and I don't know how many data I have, I don't give any parameter value to function (so by default it will take 500 data). But here I need all data so how can I manage this situation?
From https://www.codeigniter.com/user_guide/database/query_builder.html passing in NULL to the limit method as the first parameter will cause the limit statement to not limit anything - hence returning all of the rows (see on github):
public function limit($value, $offset = 0)
{
is_null($value) OR $this->qb_limit = (int) $value;
empty($offset) OR $this->qb_offset = (int) $offset;
return $this;
}
So in short if you want to skip the $limit parameter try setting its value to NULL. Like so...
public function __getalldata($tablename, $tablefield=array(), $orderbyfield = 'id', $ascdesc = 'desc', $limit = NULL, $type='result')
{
// Query generation according to above parameters
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}
I assume that the default value of $limit = 200 is already used in a number of places in the application. So the goal is to bypass the default when you want all the matching records. My suggestion is to simply pass the value 0 (zero) when you want them all. Then the only a small code change is required in the class method.
public function __getalldata($tablename, $tablefield=array(), $orderbyfield = 'id',
$ascdesc = 'desc',$limit = 200,$type='result')
{
if($limit > 0)
{
$this->db->limit($limit);
}
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
return $data->$type();
}
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = '',$type='result')
{
//Query generation according to above parameters
if($limit == '') {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
} else {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
}
return $data->$type();
}
Did you tried this?
As per MySQL documentation:
You can used bigint max values 18446744073709551615 to retrieve all data from MySQL.
So your function should look like this
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 18446744073709551615,$type='result')
{
//Query generation according to above parameters
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}
Note my main answer is at the bottom (IF you can't edit this function) but I have put in alternative answers as am unclear from OP as to if the function can/should be edited.
Edit this function
Your declared function has:
public function __getalldata($tablename,...,$limit = 200 ...){ ... }
Where the reference given in various answers here appearing to be adjusting this default value - the simplest solution is to NOT edit the default but set a special case and explicitly check for if $limit is exactly (and only) null.
To clarify:
PHP will use an explicitly set NULL value over any default value (in this case, 200).
So;
// within the function
if($limit !== null) {
//use limit in your PDO
}
This does not convert if $limit == 0 or other type-juggling cases, common to PHP
This above means that if no $limit value is given then default of 200 is used. If an explicit NULL value is given, that will be used instead.
Code
Please note this code is for example only and can probably be simplified further to better follow DRY patterns, but I don't know enough about CodeIgniter to do this here and now.
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 200,$type='result')
{
//Query generation according to above parameters
if($limit !== null){
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
}
else {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
}
return $data->$type();
}
If you can't edit the function
I'm unclear on if you can (or want) to edit the function itself, if you're using Codeigniter, so instead simply refer an automatic value to the function arguments to return every valud row, no matter what (in effect removing the case for limit.
To do this use the PHP Constant PHP_INT_SIZE or PHP_INT_MAX which will return exactly that value. Reading this question/answer tells you more about this, but these constants should be self explanatory, as the maximum int values.
If a MySQL LIMIT exceeds the rows returned, this has no effect, so by setting this to a maximal value, it will always exceed the rows returned and/or the rows usable by the resulting page output.
Therefore:
$var = __getalldata("table",[0=>'columnname'],'id','desc',PHP_INT_MAX,'result');
Will return you every row within the PHP Integer limit of [2,147,483,647] or [9,223,372,036,854,775,807], depending on your system (32bit or 64bit).
And let's face it -- if 2 billion is not enough of a limit, you need to face you have some serious problems with your SQL query ;-)
That is easy with the use of an if statement. Just check to see if a condition is true for your limit, if not resolve to else. Like so:
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 200,$type='result')
{
if () // check $limit condition here
{
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}
else {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
return $data->$type();
}
}
EDIT: I am not sure what your conditions for the $limit variable are, so just enter them in the if statement's arguments.

How to use IS NOT NULL in Associative Array

I am new in Symfony framework And I want to use Query basis on Condition
which is using associative array and I want to use IS NOT NULL But it is not
working.
$repository = $this->getDoctrine()->getRepository('AppBundle:Order');
$order = $repository->findBy(array('status' => $last_status,'new_coloumn_id'=>"IS NOT NULL"));
How to use IS NOT NULL in array.
You should add custom function into your Order Repository file(class). Example;
public function getOrderStatus($last_status = NULL){
$query = $this->createQueryBuilder('order')
->where('order.new_column_id IS NOT NULL')
->andWhere('status = :status')
->setParameter('status', $last_status);
return $query->getQuery()->getResult();
}
And you can use it;
$order = $this->getDoctrine()->getRepository('AppBundle:Order')->getOrderStatus($last_status)
Try this in your repository class:
public function findOrder($last_status){
$qb = $this->createQueryBuilder('order');
return $qb->where($qb->expr()->isNotNull('order.new_column_id'))
->andWhere($qb->expr()->eq('order.status',':last_status'))
->setParameter('last_status',$last_status)
->getQuery()
->getOneOrNullResult();//getArrayResult,getResult()
}
hope it helps...

how to use ObjectId inside $in query in MongoDB?

I have the following query in MongoDB
$this->createQueryBuilder()
->field('actor')->in($actorIdArray)
->getQuery()
->execute();
where the field actor is an object reference with annotation
#MongoDB\ReferenceOne(targetDocument="User", simple=true)
which means it will store the object Id instead of the full reference.
When $actorIdArray is an array of id with the form
["5706cb39821b166d3931f34f", "56015f7d4f8bd90b769e6e75"]
the query does not return nothing, which is the expected since the filed actor contains object id.
However, if I build the array this way
[new MongoId("5706cb39821b166d3931f34f"), new MongoId("56015f7d4f8bd90b769e6e75")]
it doesn't work either, which is quite surprising for me.
The log shows the query is made
{ "actor": {"$in":[{"$id":"5706cb39821b166d3931f34f"},{"$id":"56015f7d4f8bd90b769e6e75"}]}}
and I think it should be something like this
{ "actor": {"$in":[ObjectId("5706cb39821b166d3931f34f"),ObjectId("56015f7d4f8bd90b769e6e75"]}}
Not sure if I am doing something wrong,
any ideas?
As you can see in code source of Doctrine\ODM\MongoDB\Query\Builder -> references method:
public function references(object $document) : self
{
$this->requiresCurrentField();
$mapping = $this->getReferenceMapping();
$reference = $this->dm->createReference($document, $mapping);
$storeAs = $mapping['storeAs'] ?? null;
$keys = [];
switch ($storeAs) {
case ClassMetadata::REFERENCE_STORE_AS_ID:
$this->query[$mapping['name']] = $reference;
return $this;
.....
return $this;
}
You must know how the reference is built. Usually, this is by $id.
Use actor.$id as the field name:
$dm = $this->get('doctrine.odm.mongodb.document_manager'):
$documents = array();
foreach($actorIdArray as $id){
$documents[] = new MongoDB\BSON\ObjectId($id);
}
$this->createQueryBuilder()
->field('actor.$id')->in($documents)
->getQuery()
->execute();
Doctrine wants your array to be an array of documents.
You can load document references without query.
$dm = $this->get('doctrine.odm.mongodb.document_manager'):
$documents = array();
foreach($actorIdArray as $id){
$documents[] = $dm->getReference('AppBundle:Actor',$id); // <- this is the key
}
$this->createQueryBuilder()
->field('actor')->in($documents)
->getQuery()
->execute();

Categories