Laravel query optimization - laravel

I have a query in laravel:
...
$query = $model::group_by($model->table().'.'.$model::$key);
$selects = array(DB::raw($model->table().'.'.$model::$key));
...
$rows = $query->distinct()->get($selects);
this works fine and gives me the fields keys' that I need but the problem is that I need to get all the columns and not just the Key.
using this:
$selects = array(DB::raw($model->table().'.'.$model::$key), DB::raw($model->table().'.*'));
is not an option, cuz it's not working with PostgreSQL, so i used $rows to get the rest of columns:
for ($i = 0; $i<count($rows); $i++)
{
$rows[$i] = $model::find($rows[$i]->key);
}
but as you see this is it's so inefficient, so what can i do to make it faster and more efficient?
you can find the whole code here: https://gist.github.com/neo13/5390091
ps. I whould use join but I don't know how?

Just don't pass anything in to get() and it will return all the columns. Also the key is presumably unique in the table so I don't exactly understand why you need to do the group by.
$models = $model::group_by( $model->table() . '.'. $model::$key )->get();

Related

How to sort Eloquent collection after fetching results?

In my controller I have this query
$query = Market::whereHas('cities', function($query) use ($city) {
$query->where('id', $city);
})->get();
Then I want to make a few operations with this collection and remove my subquerys from the main object
$return['highlighted'] = $markets->where('highlighted', true);
$markets = $markets->diff($return['highlighted']);
The problem is when I try to sort it by created_at
$return['latest'] = $markets->sortByDesc('created_at')->take(4);
$markets = $markets->diff($return['latest']);
It just won't work, keeps returning the first 4 objects order by id, I've tried parsing created_at inside a callback function with Carbon::parse() and strtotime with no results.
I'm avoiding at all cost to make 2 different database querys since the original $markets has all the data that I need.
Any suggestion?
Thanks
I think your problem is you try to sort and take in one-go and also missing a small thing when you try to access the values of the collection.
Try below approach:
$return['latest'] = $markets->sortByDesc('created_at');
$markets = $markets->diff($return['latest']->values()->take(4));
Or you may need to do it like:
$return['latest'] = $markets->sortByDesc('created_at');
$return['latest'] = $return['latest']->values()->take(4);
$markets = $markets->diff($return['latest']->all());

Laravel Query Builder use multiple times

Is it possible to save a query bulider and use it multiple times?
for example, I have a model 'Tour'.
I create a long query buider and paginate it:
$tour = Tour::where(...)->orWhere(...)->orderBy(...)->paginate(10);
For example, 97 models qualify for the above query.
"Paginate" method outputs first 10 models qualifying for the query, but I also need to so some operations on all 97 models.
I don't want to 'repeat myself' writing this long query 2 times.
So I want something like:
$query = Tour::where(...)->orWhere(...)->orderBy(...);
$tour1 = $query->paginate(10);
$tour2 = $query->get();
Is that a correct way to do in Laravel? (my version is 5.4).
You need to use clone:
$query = Tour::where(...)->orWhere(...)->orderBy(...);
$query1 = clone $query;
$query2 = clone $query;
$tour1 = $query1->paginate(10);
$tour2 = $query2->get();
You can but it doesn't make any sense because every time a new query will be executed. So this code will work:
$query = Tour::where(...)->orWhere(...)->orderBy(...);
$tour1 = $query->paginate(10);
$tour2 = $query->get();
But if you want to execute just one query, you'll need to use collection methods for ordering, filtering and mapping the data. You'll also need to create Paginator instance manually:
$collection = Tour::where(...)->orWhere(...)->orderBy(...)->get();
$tour1 = // Make Paginator manually.
$tour2 = $collection;
$sortedByName = $collection->sortBy('name');
$activeTours = $collection->where('active', 1);

Skip and take all?

In eloquent, how can I skip 10 rows and then get the rest of the table?
User::skip(10)->all();
The above does not work, but it gives you an idea what I am looking for.
Try this:
$count = User::count();
$skip = 10;
User::skip($skip)->take($count - $skip)->get();
With one query:
User::skip($skip)->take(18446744073709551615)->get();
It's ugly, but it's an example from official MySQL manual:
To retrieve all rows from a certain offset up to the end of the result
set, you can use some large number for the second parameter. This
statement retrieves all rows from the 96th row to the last:
SELECT * FROM tbl LIMIT 95,18446744073709551615;
try something like this it work for sure..
$temp = User::count();
$count = $temp - 10;
$data = User::take($count)->skip(10)->get();
Laravel 5 returns Eloquent result as Collection.
So you can use collenction function slice();
$users = User::get();
$slicedUsers = $users->slice(10);

How to Join same table in laravel

I wan to wirte a join query to connect same table, and without ON, but when i write it in laravel without on it is showing error
$key = DB::table('api_keys as ak')
->join('api_keys as bk','')
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->pluck('api_key');
want to build the below query,
SELECT * FROM `api_keys` as ak
JOIN `api_keys` as bk
WHERE ak.`api_key`=$akey
and ak.`user_id`=$auser
and bk.`user_id`=$bsuer
and bk.`api_key`=$bkey
You must provide an ON clause for the join. More about where ON clauses are required can be found in this answer.
You can view the generated query using toSql() on a QueryBuilder object:
echo $key = DB::table('api_keys as ak')
->join('api_keys as bk','')
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->toSql();
Which in your case returns:
select * from `api_keys` as `ak` inner join `api_keys` as `bk`
on `` `` where `ak`.`api_key` = ? and `ak`.`user_id` = ?
In your case it isn't totally clear what you are trying to achieve, but you might consider joining on api_key or the primary key of the api_keys table, if that is different:
$key = DB::table('api_keys as ak')
->join('api_keys as bk','ak.api_key', '=', bk.api_key)
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->pluck('api_key');
DB::table('registerusers as a')
->join('registerusers as b', 'a.id', 'b.refer_id')
->where('a.username', 'b.username')
->where('b.id', 'a.refer_id')
->value('b.id');
without using on clause in laravel query builder you can use following
$key = DB::table(DB::raw('api_keys as ak, api_keys as bk'))
->where('ak.api_key', '=', $api_key)
->where('ak.user_id','=',0)
->where('ak.PK','=','bk.PK')
->pluck('ak.api_key')
where PK references to your table's primary key.
result will in your case.
select * from api_keys as ak, api_keys as bk where ak.api_key= 'api_key_value' and ak.user_id = 0 and ak.PK = bk.PK
I solved this by creating my own class and starting out with a base query which I modify to apply the join (using Laravel's joinSub function) as follows:
public function __construct()
{
$this->query = DB::table('question_responses as BASE');
}
public function applyFilter($questionId, $questionValue) {
$filterTableStr = 'filter_table_'.$questionId;
$filterIdStr = 'filter_id_'.$questionId;
$filterQuery = DB::table('question_responses AS '.$filterTableStr)
->select('survey_response_id AS '.$filterIdStr)
->where($filterTableStr.'.question_short_name', $questionId)
->where($filterTableStr.'.value', $questionValue);
$resultTableStr = 'result_table_'.$questionId;
$this->query = $this->query
->joinSub($filterQuery, $resultTableStr, function($join) use ($resultTableStr, $filterIdStr) {
$join->on('BASE.survey_response_id', '=', $resultTableStr.'.'.$filterIdStr);
});
}
After applying my required filters I can just call $this->query->get() as normal to obtain the result.
The important part was to make sure that each resulting table and join fields has unique names.
With this method I can apply unlimited filters to my base query.

Laravel4 raw statement in the middle of complex where

I have a quite complex search method which handles the $input array from a controller, the thing is that I want to perform a custom SQL statement in the middle of it, for example:
$input['myField'] = array('condition' => 'rawStatement', value => 'AND WHERE LEFT(field,9) = 10`
and that would apply into my busy-conditions-method-builder
You can see the method at
http://pastebin.com/BNUKk2Xd
I'm trying to apply it on lines 52-54 but cant seem to get it working.
I know this is an old question but looking at your pastbin you have to chain your query like.
$query = User::join('user_personal','users.id','=','user_personal.user_id');
# Join user askings
$query = $query->leftJoin('user_askings','users.id','=','user_askings.user_id');
$query = ..........
$query = $query->orderBy('users.profile_score','DESC');
$query = $query->groupBy('users.id')->paginate(32);
return $query;

Resources