How can I write this query using the laravel query builder? - laravel

I'm on laravel 5.1 using postgres as the DB. I have a fiddle here in case it helps understand my issue: https://www.db-fiddle.com/f/5ELU6xinJrXiQJ6u6VH5/4
with properties as (
select
properties.*,
json_agg(property_fields.*) as property_fields
from
properties
left join fields as property_fields
on property_fields.parent = 'property' and property_fields.parent_id = properties.id
group by properties.id, properties.deal_id, properties.address
)
select
deals.*,
json_agg(properties.*) as deal_properties,
json_agg(deal_fields.*) as deal_fields
from deals
left join properties on deals.id = properties.deal_id
left join fields deal_fields on deal_fields.parent = 'deal' and deal_fields.parent_id = deals.id
group by deals.id, deals.name
order by deals.id
Writing most of this is fairly straight forward. The problem I'm having is with the with properties as (...) block. I've tried something like:
DB::statement('WITH properties AS ( ... )')
->table('deals')
->select(' deals.*, json_agg(properties.*) as deal_properties, ')
...
->get();
But I notice the execution stop after DB::statement()
Is there a method in the Query Builder that I'm missing? How can I prefix my query with the WITH properties AS (...) statement?
I think it should also be noted that I'm trying to implement a Repository Pattern and I can't just wrap a DB::statement() around the whole query.

I've created a package for common table expressions: https://github.com/staudenmeir/laravel-cte
$query = 'select properties.*, [...]';
DB::table('deals')
->withExpression('properties', $query)
->leftJoin([...])
->[...]
You can also provide a query builder instance:
$query = DB::table('properties')
->select('properties.*', [...])
->leftJoin([...])
->[...]
DB::table('deals')
->withExpression('properties', $query)
->leftJoin([...])
->[...]

if you want some data fetch from a table you can use this type of code
$user = DB::table('table name')->where('name', 'John')->where('height','!>',"7")->select('table fields which you want to fetch')->get();
Or try using the larevel Eloquent ORM which will make things easier with the database.
for more example or reference
https://laravel.com/docs/5.0/queries

I think you can actually do this with eager loading,
assuming that the relationships are set up correctly.
(More reading here: https://laravel.com/docs/5.4/eloquent-relationships#constraining-eager-loads)
So I think you'd be able to add something like
->with(['properties' => function ($query) {
$query->select('field')
->leftJoin('join statement')
->groupBy('field1', 'field2');
}])

Related

How can i convert this sql query in a eloquent laravel command?

in sql query this commando do exactly i wanted:
SELECT
v.id,
(
SELECT sv.status_id
FROM status_viagem sv
WHERE sv.viagem_id = v.id
ORDER BY sv.created_at DESC LIMIT 1 ) AS status_id
FROM viagens v
Here is the sql results:
But i have no idea how can i do this using Laravel eloquent
Basically, a viagem entry can has a lot of status, but i need to get each viagem and their last status entry from status_viagem table (the pivot table)
by the way viagem/viagens means travel.
My class mapping:
class Viagem extends Model
{
...
public function status()
{
return $this->belongsToMany('App\Status')->withTimestamps();
}
...
}
class Status extends Model
{
public function viagens()
{
return $this->belongsToMany('App\Viagem')->withTimestamps();
}
}
The belongsToMany at both classes gets me a many-to-many:
can someone help me? thanks
---------- Temporary Solution -------
Thanks for all help guys. In fact i can't find a nice solution using only eloquent.
Step 1/3 - To bypass this situation i first execute the above sql to grab only the viagens under the desired status_id (last status_viagem entry):
$viagens_ids = DB::select(
"SELECT viagem_id FROM (
SELECT
v.id AS viagem_id,
(
SELECT sv.status_id
FROM status_viagem sv
WHERE sv.viagem_id = v.id
ORDER BY sv.id DESC LIMIT 1 ) AS status_id
FROM viagens v
) AS tt
WHERE tt.status_id = {$status->id}"
);
Step 2/3 - then i used the array_map to organize my viagens ids
$a = array_map(
function($obj) { return $obj->viagem_id; },
$viagens_ids
);
Step 3/3 - And at last i used elequent whereIn to fetch my viagens:
Viagem::with( 'status')->whereIn('id', $a)->get();
In fact i have solved the problem by a-old-sashion-way but i not happy with it because i wish i learn how to do it using eloquent. what bad to me.
There are many ways to query in laravel. I have created a test project for you to try. The gist are:
1. Eloquent ORM
Eloquent ORM is Laravel's magic which have some limitations in eager loading - which i just come across while contemplating your question for hours. It wont play nicely with first(), last(), and some more functions in the constrained eager loading closure.
In your case, our almost there can be fixed:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
It will return entire field for Viagem and Status including its pivot table (the status_viagem).
However, if you wanted to retrieve only viagem.id and status_viagem.status_id, you can map() it as such:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
->map(function($data){
$o = new stdClass();
$o->id = $data->id;
$o->status_id = $data->status->first()->id;
return $o;
});
Please take note that the statement above require sql query to be ran twice. Eager loading basically works by querying all the Viagem first then queries the Status and map them in memory based on the foreign keys. You can observe that replacing get with toSql will only give you the first query. Please enable Query Logging to see the second query.
2. Query Builder
Embarking from Ryan Adhitama Putra answer, you could do something like:
App\Models\Viagem::join('status_viagem', 'viagens.id', '=', 'status_viagem.viagem_id')
->orderBy('status_viagem.created_at', 'desc')
->groupBy(['status_viagem.status_id', 'viagens.id'])
->select(['viagens.id', 'status_viagem.status_id'])
->get();
This query builder approach guaranteed to be ran only once, you can replace the get() with toSql() to see the resulting query.
3. Raw Queries
Throwing DB::raw() can help sometime, but i really did not want to mention it.
I am not sure what viagens and viagem represent, but I think one of the relationships has to be belongsToMany() and the other hasMany().
then after you set relationships correctly, you can use Eloquent like this :
$status_id = Viagem::with('status')->orderBy('created_at', 'desc')->first()->pluck('status_id');
Try this.
$status_id = Viagem::join('status','viagem.id','status_viagem.viagem_id')
->select('viagem.id','status_viagem.status_id')
->get();

How to filter some database records with Join in Laravel

I'm working with Join in laravel, I'm getting result in one part, but now I need to pick up only some records that are active but I can not implement. My query would be this:
$queryBuilder = $query->parse(
$queryBuilderJSON, DB::table('tab_person')
->where('deleted_at', NULL)
->leftJoin('tab_user', 'tab_person.pac_r', '=', 'tab_user.reg_id'));
// 'tab_user.reg_status', '=', 'A' **I can not implement this condition, how can I do it?**
Ignore queryBuilderJSON, I'm working with it to use Query Builder Jquery, which is a search library. What I just need to know is how to filter the USER records that are only active (they are represented by the letter 'A') and the line is commented out in the code. Thanks in advance!
You can add multiple join conditions using a closure:
->leftJoin('tab_user', function ($query) {
$query->on('tab_person.pac_r', '=', 'tab_user.reg_id');
$query->where('tab_user.reg_status', '=', 'A');
});
You can learn more on advanced join clauses in the Laravel Docs.

How do I get a "select count(*) group by" using laravel eloquent

I would like to execute the follow sentence using laravel eloquent
SELECT *, count(*) FROM reserves group by day
The only solution occurs to me is to create a view in the DB, but I am pretty sure there is a way to do it in laravel way.
You could use this:
$reserves = DB::table('reserves')->selectRaw('*, count(*)')->groupBy('day');
As you wish to do it with Laravel Eloquent I assume you have a model name Reserve. In this case you can use this
$reserve = Reserve::all()->groupBy('day')->count();
You could use:
#Laravel Raw Expressions
$reserves = DB::table('reserves')
->select(DB::raw('count(*) as reserves_count'))
->groupBy('day')
->get();
OR
$reserves = Reserve::select(['reserves.*'])
->groupBy('day')
->count();
Further read here

Where Is My Raw Query?

Here is my controller code;
$temp_table_data = $temp_table
->setTempTable($generated_temp_table)
->newQuery()
->with(['payment' => function ($query) use ($column_values) {
$query->select($column_values);
}])->get();
My toSql query is right below it;
$sql = str_replace(['%', '?'], ['%%', "'%s'"], $temp_table->toSql());
$fullSql = vsprintf($sql, $temp_table->getBindings());
print_r($fullSql);
My code prints out;
select * from `selected_postcodes_1434968225_1`
Where are the details of the payments information that I am "with"ing? If I want to print out the raw query now, to show another developer, to get some help, what am I supposed to do here?
The eager loaded relationships are fetched in a separate query. You can use DB::getQueryLog() to get all run queries. Note that you have to enable it first using with DB::enableQueryLog().
Another alternative is the Laravel Debugbar package that shows you all queries and much more.

Laravel Query Builder: whereExists translates condition clause to question mark

If I have the following query built using the Query Builder:
$q = DB::table('Products')->whereExist(function ($q)
{
$q->select(DB::raw(1))
->from('tags_products')
->where('products.PorductId', '=', 'tags_products.ProductID');
});
The translated SQL using $q->toSql(); that is:
select * from `Products` where `exist` = (select 1 from `tags_products` where `products`.`ProductID` = ?)
Apparently, the Query Builder translates tags_products.ProductID to ?.
Why does it become "?" ?
As #Jared Eitnier very well pointed out, Laravel uses PDO to bind the parameters you pass to the Query Builder methods. However, because when you use where the third parameter represents the value, Laravel will not treat it as a column unless you explicitly tell it to, otherwise it will treat 'tags_products.ProductID' as a regular string value. So you have two options here:
1. Use DB::raw() to let the Query Builder know that the value is not a string that requires escaping:
->where('products.PorductId', '=', DB::raw('tags_products.ProductID'));
2. Use whereRaw() which will allow you to write a raw SQL statement:
->whereRaw('products.PorductId = tags_products.ProductID');
These are mysql prepared statements.
See What is the question mark's significance in MySQL at "WHERE column = ?"?
Laravel uses mysql's PDO.

Resources