Many-to-Many and Eager loading query - laravel

I am new with Laravel, I was able to query Many-to-Many relationships. Where 'template_dynamic' is the pivot of two tables 'template' and 'dynamic'.
// Template Model
class Template extends Eloquent
{
protected $table = 'template';
protected $guarded = array('template_id');
protected $primaryKey = 'template_id';
public function dynamic()
{
return $this->belongsToMany('dynamic', 'template_dynamic')
->select('*')
->withPivot('template_dynamic_id')
->orderBy('template_dynamic_html_sort', 'ASC');
}
here I was able to retrieve the records
// Template Controller
$dynamic_fields = Template::find($rec->template_id)->dynamic;
what I want to do now is that pivot table has-many properties 'template_dynamic_option'. How will I query the records and combine it with $dynamic_fields variable?
// What I want to do is something like this. But sadly this is an invalid syntax
$dynamic_fields = $dynamic_fields->with('template_dynamic_option');
Any recommendation or enhancements are welcome.
Thank you in advance.

First, I am pretty sure you don't need the select('*') in your relationship query there.
But let's get to your actual problem ;)
To access the pivot table in Eloquent is pretty simple.
$dynamic_fields = Template::find($rec->template_id)->dynamic;
foreach($dynamic_fields as $dynamic){
var_dump($dynamic->pivot)
}
The thing is though, by default only the keys of the pivot table are present in the object.
To change that you have to define them with withPivot(). Actually like you already did but not with the id.
public function dynamic()
{
return $this->belongsToMany('dynamic', 'template_dynamic')
->withPivot('template_dynamic_option')
->orderBy('template_dynamic_html_sort', 'ASC');
}
And if you have multiple additional columns use this syntax:
->withPivot('template_dynamic_option', 'foo', 'bar');

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

Laravel - can I make models dynamically and have relationships on each generated model?

I need to create Models based on the alphabet as their tables names are like 'products_a', 'products_b'....'products_z'. But I don't want to create all these models as separated files,
but want to find a way to use all these tables dynamically.
And all tables are separated based on some 'shopping-mall id' values.
I've found a solution to set tables dynamically like this solution.
So what I tried before was like below.
class Products extends Model
{
use BindsDynamically;
public $timestamps = false;
public $fillable = [ 'product_name', 'reg_time'];
public static function newProduct($mall_id)
{
$rangeArr = range('a', 'z');
$product = new Products;
foreach ($rangeArr as $ar) {
if (strtolower(substr($mall_id, 0, 1)) == $ar) {
$product->setTable('product_'.$ar);
}
}
return $product;
}
}
However, now I created a Cart model and tried to use the relationship methods with all the separated tables which I cannot.
So I realized I need the separated models with relationships not just tables.
Theoretically, my database has those tables.
products_a
products_b
....
products_z
products_cart
So, the products_cart table needs to have all the products tables idx data.
I want to use relationships like 'hasMany' or 'belongsTo', therefore I need all the separated models.
Can I make all the alphabetical models dynamically and use relationship methods?
If so, how can I do this?
OK, I understand we have a nasty DB design here. But then, we don't have a DB expert and I cannot figure what can I do about this DB design.
So, please don't judge about the DB design, rather guide the better way to replace it.
Maybe you UNION all product tables and overwrite the models newModelQuery() method to achieve that
class Products extends Model
{
use BindsDynamically;
public $timestamps = false;
public $fillable = [ 'product_name', 'reg_time'];
/**
* Get a new query builder that doesn't have any global scopes or eager loading.
*
* #return \Illuminate\Database\Eloquent\Builder|static
*/
public function newModelQuery()
{
$query = parent::newModelQuery();
$rangeArr = range('a', 'z');
foreach ($rangeArr as $ar) {
//if (strtolower(substr($mall_id, 0, 1)) == $ar) {
$query->union('product_'.$ar);
//}
}
return $query;
}
}
But that's probably just one of a couple methods you'd have to overwrite this way. And this solution currently doesn't support your $mall_id. But after all, it's due to bad database design.

Retrieve all columns from table A and some from table B using laravel eloquent

I am trying to retrieve the thumb image path by joining the images table to the listing table. As such, I have the following query in my controller.
$listings = Listing::select('listings.*, images.path as image_path')
->where('listings.ownerid', '=', $ownerid)
->leftJoin('images', 'listings.thumbId', '=', 'images.id')->get();
After testing out the function, the query fails since laravel interprets the query as
select `listings`.`*, images`.`path` as `image_path` from `listings` left join `images` on `listings`.`thumbId` = `images`.`id` where `listings`.`ownerid` = 1)
Notice the asterisk (*) is joined with the ", images" word making it '*, images'.
The query works fine without laravel's odd typo. How does one fix this issue?
You need to do one change in your query. You are passing raw select fields so you need to use selectRaw() instead of select(). Like
$listings = Listing::selectRaw('listings.*, images.path as image_path')
->where('listings.ownerid', '=', $ownerid)
->leftJoin('images', 'listings.thumbId', '=', 'images.id')->get();
check by try above query.
I suggest you to use Laravel Eloquent Relationships feature. Since your code above is more like Query Builder rather than Eloquent. Let's see the example bellow:
You will have 2 Models, 1 for each table (listings, images):
App\Listing Model:
<?php
...
use App\Image;
class Listing extends Eloquent {
...
protected $table = 'listings';
// define Eloquent Relationship of Listing model to Image model
public function image() {
return $this->belongsTo(Image::class, 'thumbId');
}
...
}
App\Image Model:
<?php
...
use App\Listing;
class Image extends Eloquent {
...
protected $table = 'images';
...
// define Eloquent Relationship of Image model to Listing model
public function listings() {
return $this->hasMany(Listing::class, 'thumbId');
}
}
So how to get the data?
// get all listing data
$listings = Listing::all();
// loop through the data
foreach ($listing as $listing) {
dump($listing->id);
// because we have define the relationship, we can access the related data of image
dump($listing->image->path);
// call $this->image will return related Image model
dump($listing->image);
}
You can see Laravel official documentation for more example and explanation.
Hope it helps.

Laravel ORM should return Relation

Is it possible to return an value for an hasOne relation directly with an Model?
For example:
$List = Element::orderBy('title')->get();
The Element has a "hasOne" Relation to an column:
public function type()
{
return $this->hasOne('App\Type', 'id', 'type_id');
}
How can i now return automatically the "type" for the Model?
At the Moment i am looping through all Elements, and build my own "Array" of Objects, including the Key of "type" in this example. But ill prefer to do this only in my Model.
Ill know how to add a "normal" property, but can it be someone from an relation?
public function getTypeAttribute($value)
{
return // how ?
}
protected $appends = array('type');
Is this possible?
Edit:
A workaround could be to use DB:: to return the correct value - but ill dont thing thats a good workaround: like:
public function getTypeAttribute($value)
{
// make a query with $this->type_id and return the value of the type_name
}
protected $appends = array('type');
you need to eager load your relations when getting the Element:
$list = Element::with('type')->orderBy('title')->get();
then access the type using
foreach ($list as $item) {
echo $item->type->type_name;
}
where type_name would be the name of a column in the types table
Make a query scope and in that scope, join your attributes from other tables.
http://laravel.com/docs/5.1/eloquent#query-scopes
http://laravel.com/docs/5.1/queries#joins

Laravel eloquent query (one to one)

I have two tables, tracks and echonestrecords. Tracks have one record in the echonestrecords table with information about the track.
tracks has these columns:
id, title, artist, created_at, updated_at
echonestrecords has these columns:
id, track_id, tempo, mode, key
The track object has the hasOne()-function to return the echonestrecord.
The echonestrecord object has the belongsTo()-function to return the track.
I want to make an Eloquent-query to get all tracks which have an empty row in the echonestrecords-table. That is, a row there the tempo is null.
I can do it like this with the raw query, which doesn't return Track objects.
$tracks = DB::table('tracks')
->select('tracks.*')
->join('echonestrecords','echonestrecords.track_id','=','tracks.id')
->where('echonestrecords.tempo',null,'NULL')
->get();
But I can't figure out how to do this with Eloquent. I figured it must be something like:
$tracks = (array('echonestrecord' => function($query) {
$query->where('tempo', null, 'NULL');
}))->get();
But this returns all tracks! I'm confused.
Can you help me?
I solved it. Yahooo..
The answer was using whereHas:
$tracks = Track::whereHas('EchoNestRecord', function($q)
{
$q->whereNull('tempo');
})->paginate(3);
What you probably need is to create a relation and filter it a little bit more:
class EchoNestRecord extends Eloquent {
protected $table = 'echonestrecords';
}
class Track extends Eloquent {
protected $table = 'tracks';
public function RecordsWithNoTempo()
{
return $this->hasOne('EchoNestRecord')->whereNull('tempo');
}
}
Then you should be able to:
$track = Track::find(1);
foreach($track->recordsWithNoTempo as $record)
{
echo $record->name;
}
Use
dd(DB::getQueryLog());
To check the generated query.

Resources