Preserve Order of IN in ORM Order - phalcon

I'm trying to do a query where I preserve the order of the ids in a IN statement. I can't seem to do it with either the Model Manage Query Builder or the standard ORM 'order' array parameter. Am I missing something? I keep getting:
UNEXPECTED TOKEN IDENTIFIER(, NEAR TO 'id`enter code here`,17743,16688,16650
Here's my model manager:
$query = $this->modelsManager->createQuery('SELECT * FROM Projects WHERE id IN ('.implode(',', array_keys($finalIterations)).')
ORDER BY FIELD(id,'.implode(',', array_keys($finalIterations)).'');
It's pretty obvious PhQL doesn't like the FIELD key word. Is there a way for me to do what I'm trying to do with PhQL? It seems I will not be able to do what I need to.

Unfortunately as previously said, this is missing a feature in Phalcon.
Have a look at this function, I've put it into my ModelBase abstract class which is parent class of all my models. It uses PhQL variable binding, so it's safe for handling direct user input.
You could have reimplemented custom \Phalcon\Mvc\Model\Criteria but this solution seems to be easier to work with, at least for me.
ModelBase abstract
public function appendCustomOrder( \Phalcon\Mvc\Model\CriteriaInterface &$criteria, $orderField, array &$orderValues = [] ) {
if(!empty($orderValues)) {
$queryKeys = $bindParams = [];
foreach($orderValues as $key => $id) {
$queryKey = 'pho'.$key;
$queryKeys[] = ':'.$queryKey.':';
$bindParams[$queryKey] = $id;
}
// TODO: add support for multiple orderBy fields
$criteria->orderBy('FIELD('.$orderField.','.implode(',',$queryKeys).')');
// there's no 'addBind' function, need to merge old parameters with new ones
$criteria->bind( array_merge( (array) #$criteria->getParams()['bind'], $bindParams ) );
}
}
Controller usage
$projectIDs = [17743, 16688, 16650];
$projectsModel = new Projects();
$criteria = $projectsModel->query->inWhere( 'id', $projectIDs );
$projectsModel->appendCustomOrder( $criteria, 'id', $projectIDs );
$projectsData = $criteria->execute();
This will generate valid PhQL syntax similar to this one:
SELECT `projects`.`id` AS `id`, `projects`.`title` AS `title`
FROM `projects`
WHERE `projects`.`id` IN (:phi0, :phi1, :phi2)
ORDER BY FIELD(`projects`.`id`, :pho0, :pho1, :pho2)

Related

Typo3 9.5 - Custom flexform ordering, wrong backquotes in sql

i have an custom extension, where you can select the different entries at the backend, to show them at the list view. I have a custom sorting at my backend, but the system always sort them Descending.
I implemented an "orderBy" function, which doesnt work, because the system uses wrong backspaces.
My code looks like this:
I call the sort function in my "findByUid($uid)" function like this:
$query->setOrderings($this->orderByKey('uid', $uidArray));
protected function orderByKey($key, $uidlist) {
$order = array();
foreach ($uidlist as $uid) {
//$order["$key=$uid"] = \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING;
$order["$key=$uid"] = "ASC";
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($order);
}
return $order;
}
The result at the sql query is:
ORDER BY `tx_MYEXTENSION_domain_model_FIELD`.`uid=3` DESC
But it must be:
ORDER BY `tx_MYEXTENSION_domain_model_FIELD`.`uid` = 3 DESC
Is there a way to change this?
After a lot of search, I found this solution on an stackoverflow entry:
$ids = explode(',',$this->settings['entries'])
foreach($ids as $key => $id){
$entries[$id] = $this->entriesRepository->findByUid($id);
}
This code snippet has to be intergrated at the controller.
For me its working.

Laravel/SQL: How to fetch data from multiple table in a single query? that too using 'where'

Working on a search functionality on Laravel App(Blog/Posts).
There are multiple types of posts (each having a separate table in the database)
Like Business posts, Social Life posts etc..
Below is the search function on SearchController
class SearchController extends Controller
{
public function search(Request $request, $query = null)
{
if($query == null)
return redirect()->route('home');
$search = Business::where([['title','like','%'.$query.'%'],['status','=',1]])
->orWhere([['description','like','%'.$query.'%'],['status','=',1]])
->paginate(10);
return view('front.search',[
'results' => $search,
'query' => $query
]);
}
}
So basically my question is how to add other types of Post's table also?
My main motive is that when someone searches for anything, the result should be fetched from all types of posts table(business, nature, life & so on..).
You have to maintain common id in both the table
NOTE: Join is the preferable method
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);
If you have a model called Book, like this:
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
Then you can retrieve all of your books with authors like this:
$books = App\Book::with(['author'])->get();
Check out Eager loading from Laravel documentation.
Just add table name before every field
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);

Symfony2 + Doctrine - Filtering

I've got a OneToMany relationship where one football team has many players. I want to list all football teams and display the name of the captain for each team.
Each player entity has a foreign key (team_id) and a field 'captain' which is set to 0 or 1. I'm currently running the following query:
$teams = $this
->getDoctrine()
->getRepository('FootballWebsiteBundle:Team')
->createQueryBuilder('t')
->setFirstResult(($pageNumber * $resultPerPage) - $resultPerPage)
->setMaxResults($resultPerPage)
->add('where','t.deleted = 0')
->add('orderBy', 't.name DESC')
->getQuery()->getResult();
Then when I loop through each team in twig I run team.getTeamCaptain().getName() which is a filter within my Team entity:
public function getTeamCaptain() {
$them = $this->players->filter(function($p) {
return $p->getCaptain() == 1;
});
return $them->first();
}
Is there a better way to run this query?
First of all, you may want to fetch-join the players of each retrieved team to avoid having them lazy loaded during rendering of the template. Here's the DQL:
SELECT
t, p
FROM
FootballWebsiteBundle:Team t
LEFT JOIN
t.players p
WHERE
t.deleted = 0
ORDER BY
t.name DESC
Which can be built with following query builder API calls:
$teamsQuery = $this
->getDoctrine()
->getRepository('FootballWebsiteBundle:Team')
->createQueryBuilder('t')
->addSelect('p')
->leftJoin('t.players', 'p')
->add('where','t.deleted = 0')
->add('orderBy', 't.name DESC')
->getQuery()
Then you wrap this query into a Paginator object (since setMaxResults and setFirstResult cannot be trusted when fetch-joining):
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($teamsQuery, true);
$teamsQuery
->setFirstResult(($pageNumber * $resultPerPage) - $resultPerPage)
->setMaxResults($resultPerPage)
In your view you can then iterate on the teams like following pseudo-code:
foreach ($paginator as $team) {
echo $team->getTeamCaptain() . "\n";
}
You can also gain some extra performance in your getTeamCaptain method by using the Selectable API:
public function getTeamCaptain() {
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->andWhere($criteria->expr()->eq('captain', 1));
return $this->players->matching($criteria)->first();
}
The advantage here is mainly relevant when the association players is not yet initialized, since this will avoid loading it entirely. This is not the case, but I consider it a good practice (instead of re-inventing collection filtering logic).

Yii CDbCommand create query

I can't create the following query using Yii:
SELECT recipientId as broadcasterId, SUM(quantity) as quantity FROM `creditlog`
WHERE websiteId=3 AND timeAdded>='2013-01-17'
AND timeAdded<='2013-02-17'
AND recipientId IN (10000024, 10000026, 1000028) GROUP BY `recipientId`
I tried:
$command = Yii::app()->db->createCommand();
$command->select('recipientId as broadcasterId, SUM(quantity) as quantity');
$command->from('creditlog');
$command->where('websiteId=:websiteId AND timeAdded>=:dateStart AND timeAdded<=:dateEnd AND recipientId IN (:recipients)',array(':websiteId' => $websiteId, ':dateStart' => $dateStart, ':dateEnd' => $dateEnd, ':recipients' => $broadcasterIds));
$command->group('recipientId');
also the andWhere() function which is in the docs seems to be missing.
The issue is that IN condition but I can't find a way to rewrite it.
Since you don't have access to andWhere, which would make life much simpler, you have to express the parameters with where like this:
$command->where(array(
array('and',
'websiteId=:websiteId',
array('and',
'timeAdded>=:dateStart',
array('and',
// ...
), $parameters);
This is done so that you can at some point use the proper array('in', 'recipientId', $values) syntax to produce the IN(...) SQL.
However, this is ugly and difficult to manage. As long as all the conditions are simply joined together with AND you can construct the data structure programmatically from a saner data representation like this (in effect this is a workaround for the missing andWhere):
$conditions = array(
'websiteId=:websiteId',
'timeAdded>=:dateStart',
'timeAdded<=:dateEnd',
array('in', 'recipientId', $broadcasterIds),
);
$where = null;
foreach ($conditions as $condition) {
if (!$where) {
$where = $condition;
}
else {
$where = array('and', $where, $condition);
}
}
$command->where($where, $parameters);
For more information on why this way of expressing things has to be used you can refer to the documentation for CDbCommand::where.

Dynamic PDO AND and OR

I have a problem with Yii's CDBCrtieria builder. I am trying to make a rather complex query while using the escaping and safe functions provided by PDO.
Here is the query I am basically trying to build:
SELECT * FROM tbl_audit_log
WHERE (model_id = 1 AND model = "Title") OR
(model_id = 1 AND model = "Product") //etc
This is being built dynamically in PHP like:
$model_ids = array(array($model->id, 'Title'));
foreach($model->products as $id => $product){
$model_ids[][] = $product->id;
$model_ids[][] = "Product";
}
So I don't know the values of the WHERE before I build the query. I must find an easy way to build:
WHERE (model_id = 1 AND model = "Title") OR
(model_id = 1 AND model = "Product") //etc
Dynamically.
I have looked through the documentation but the closest thing I see is addCondition which would require complex coding to get working properly.
Does Yii provide any easy way of achieving this without having to deal with writing complex code to name my params etc?
It is indeed a bit more complex, but here's a working solution:
$criteria = new CDbCriteria();
$param_id = 0;
// $model_ids is the one you built in your original code
foreach( $model_ids as $id_pair ) {
$criteria->addCondition( '( model_id = :id' . $param_id . ' AND model = :model' . $param_id . ' )', 'OR' );
$criteria->params[ ':id' . $param_id ] = $id_pair[0];
$criteria->params[ ':model' . $param_id ] = $id_pair[1];
$param_id++;
}
This will generate custom identifiers for each of your parameters so they will all be validated. Then you can just use $criteria in your query.