Select only certain columns in related model when eager loading - laravel

I have two related models:
class Attribute extends Eloquent
{
public function categories()
{
return $this->hasMany('Category');
}
}
class Category extends Eloquent
{
public function attributes()
{
return $this->belongsTo('Attribute');
}
}
I want to return all attributes and their categories as a JSON object, but I only want to select certain fields in both models (i.e. not return fields like 'created_at' in the JSON).
I've tried this:
$attributes = Attribute::select('id', 'name')
->with(['categories' => function ($query) {
$query->select('categories.id', 'categories.name', 'categories.attribute_id');
}]);
Response::JSON($attributes->get());
but despite the select query for the related model, unrequested fields are returned:
attributes: [{
id: 3,
name: "Organisation",
categories: [{
id: 3,
name: "Asia HQ",
attribute_id: 3,
created_at: "2013-11-30 00:00:00",
updated_at: "2013-11-30 00:00:00"
}]
}]
How do I select only certain columns in related model when eager loading?

What about an eloquent method that will look like this?
public function dynamicAttributes($array_columns) {
return $this->belongs_to('User')->select((array) $array_comuns);
}
Where $array_columns could be a string or array of strings representing a columns you want?

Make use of select() laravel method:
$attributes = Attribute::select('id', 'name')->with(['categories' =>function($query){
$query->select(['categories.id', 'categories.name','categories.attribute_id'])->get();
}]);
Response::JSON($attributes->get());
To make select method work in eager loading you need to include foreign_key in
selected column list.
Reference : http://laravel-tricks.com/tricks/column-selection-in-eager-loading

If you want to always return those specific fields from the categories relation, defining the select in the relationship will work, but if you would like to do it on the fly for one specific query, ask Eloquent to only return those fields.
$attributes = Attribute::with(['categories' => function ($query) {
$query->select('id', 'name', 'attribute_id');
}])->get('id', 'name');
... Or you could use Fluent
DB::table('attributes')
->join('categories', 'attributes.id', '=', 'categories.attribute_id')
->select('attributes.id', 'attributes.name', 'categories.id', 'categories.name', 'categories.attribute_id')
->get();

Try this:
$attributes = Attribute::select('id', 'name')
->with([
'categories:categories.id,categories.name,categories.attribute_id',
]);
Response::JSON($attributes->get());
Please notice: no space after comma in argument list

Related

I want to change my laravel database query to model relationship query for search data from database

I am learning laravel Eloquent relationship, I successfully created a relationship between models for eq city belongsTo State and belongsTo country.
public function state(){
return $this->belongsTo(State::class,'state_id', 'sr_id');
}
public function country(){
return $this->belongsTo(Country::class,'country_id', 'sr_id');
}
this is my old search code, I want to know how we search data using model relationship method, how we call each columns from their following table.
$city = city::leftjoin('master_country','master_country.sr_id','=','master_city.country_id')
->leftjoin('master_state','master_state.sr_id','=','master_city.state_id')
->where('master_city.sr_status',0)
->where('master_city.sr_id','LIKE','%'.$query.'%')
->orWhere('master_city.sr_name','LIKE','%'.$query.'%')
->orWhere('master_city.city_tel_code','LIKE','%'.$query.'%')
->orWhere('master_country.sr_name','LIKE','%'.$query.'%')
->orWhere('master_state.sr_name','LIKE','%'.$query.'%')
->orderBy('master_city.sr_id')
->paginate(3,array('master_city.*','master_country.sr_name AS c_name','master_state.sr_name As s_name'));
so it would we like..
City::with('state','country')->where etc
You can have a look at Constraining Eager Loads
$cities = City::with(
[
'state' => function ($query) {
$query->where('state_column', 'some_value');
},
'country' => function ($query) {
$query->where('country_column', 'some_value');
}
]
)->get();
If you don't want to retrieve the relation data, you can use the whereHas() method:
City::whereHas('state', fn (Builder $builder) => $builder->where('state_column', 'some_value')));
Using arrow function is not required.
More documentation on this

Laravel - Get array with relationship

I have an ajax call that returns an array:
$reports = Report::where('submission_id', $submissionID)
->where('status', 'pending')
->get(['description','rule']);
return [
'message' => 'Success.',
'reports' => $reports,
];
From this array, I only want to return the fields 'description' and 'rule'. However I also want to return the owner() relationship from the Report model. How could I do this? Do I have to load the relationship and do some kind of array push, or is there a more elegant solution?
You can use with() to eager load related model
$reports = Report::with('owner')
->where('submission_id', $submissionID)
->where('status', 'pending')
->get(['id','description','rule']);
Note you need to include id in get() from report model to map (owner) related model
you will have probably one to many relationship with Reports and owners table like below
Report Model
public function owner() {
return $this->belongsTo('App\Owner');
}
Owner Model
public function reports() {
return $this->hasMany('App\Report');
}
your controller code
$reports = Report::with('owner')->
where('submission_id', $submissionID)->where('status', 'pending')->get()
return [
'message' => 'Success.',
'reports' => $reports,
];
This is what I ended up going with:
$reports = Report::
with(['owner' => function($q)
{
$q->select('username', 'id');
}])
->where('submission_id', $submissionID)
->where('status', 'pending')
->select('description', 'rule','created_by')
->get();
The other answers were right, I needed to load in the ID of the user. But I had to use a function for it to work.

mb_strpos() expects parameter 1 to be string, object given when querying 2 tables in Laravel

Ok, so I don't know if this can be done, but I need to combine the results of 2 Where clauses from 2 tables into one variable.
So far I have this working to query one table:
$allCompanies = Products::where('category_id', $id->id)->groupBy('company_id')->get();
And this for the other:
$companies = Company::where('visible', 0)->get();
But is there any way to get them into the same query string? Something like so I can get where the ID matches the ID column in one table AND where visible is 0 in the other?
I tried this:
$allCompanies = Products::with('company', function($q){
$q->where('visible', 0);
})->where('category_id', $id->id)
->groupBy('company_id')->get();
but got this error:
mb_strpos() expects parameter 1 to be string, object given
Company Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
public function products()
{
return $this->hasMany('App\Products');
}
}
Products Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Products extends Model
{
protected $fillable = [
'name',
'description',
'image',
'size',
'price',
'stock',
'company_id',
'category_id'
];
public function category()
{
return $this->belongsTo('App\Categories', 'category_id');
}
public function company()
{
return $this->belongsTo('App\Company', 'company_id');
}
}
When using callback in with, you should pass callback as value of associative-array.
Your query should be:
$allCompanies = Products::with(['company' => function($q) {
$q->where('visible', 0);
}])
->where('category_id', $id->id)
->groupBy('company_id')
->get();
Ref: https://laravel.com/docs/8.x/eloquent-relationships#nested-eager-loading-morphto-relationships
The most Laravel way of achieving this is through Relationships and whereHas. If you have a relationship defined you could do the following:
$products = Products::where('category_id', $id->id)
->whereHas('company', function($q) {
$q->where('visible', 0);
});
Which will query for all the products with a certain category_id that also have a relationship with a company that has a column visible with value 0
Depends on what you want:
If you want all the products from visible companies:
$products = Products::whereHas('company', function($q) {
$q->where('visible',0);
})->get();
If you want all the companies with their products (which I would advise):
$Companies = Company::with('products')->where('visible',0)->get();

How to join 3 tables with Laravel's Eloquent relationships with Eager Loading?

So, an order has a foreign_key offer_id.
And an offer has a foreign_key item_id.
An item may be in multiple offers. But every offer has one item.
An offer may be in multiple orders. But every order has one offer.
When I do this:
$orders = Auth::user()->orders()
->with('offer')
->get();
I get this:
id: 3,
user_id: 1,
offer_id: 5,
created_at: "2019-02-15 00:40:31",
updated_at: "2019-02-15 00:40:31",
offer: {
id: 5,
item_id: 3,
created_at: "2019-02-15 00:39:40",
updated_at: "2019-02-15 00:39:40"
}
As you can see, I can get that for this offer the item_id: 3
But I want to get the whole item; all its columns, not just the id.
Normally, you would join these two tables.
How to do this with Eloquent?
Here are my eloquent relationships:
Order
public function offer()
{
return $this->belongsTo(Offer::class);
}
Offer
public function orders()
{
return $this->hasMany(Order::class);
}
public function item()
{
return $this->hasOne(Item::class);
}
Item
public function offers()
{
return $this->belongsToMany(Offer::class);
}
Laravel Eager Loading
Underneath this you will find nested eager loading:
To eager load nested relationships, you may use "dot" syntax. For example, let's eager load all of the book's authors and all of the author's personal contacts in one Eloquent statement:
$books = App\Book::with('author.contacts')->get();
In your case, we can get nested ones using the dot notation between relationships.
$orders = Auth::user()->orders()
->with(['offer', 'offer.item'])
->get();
If by food you mean items in an order, how about :
$orders = Auth::user()->orders()
->with('offer', 'offer.orders', 'offer.item')
->get();
The answers from both #MihirBhende and #swonder pointed in the right way.
It should indeed be:
$orders = Auth::user()->orders()
->with('venue')
->with('offer.item')
->get();
or (same thing):
$orders = Auth::user()->orders()
->with(['venue', 'offer.food'])
->get();
But the relationship of the Offer and Item models should be reversed:
Offer
public function item()
{
return $this->belongsTo(Item::class);
}
Item
public function offers()
{
return $this->hasMany(Offer::class);
}

Advanced Select statement (Laravel)

In the Products table i have the store_id column which connects the product with the store.
In my APIController which i use with Angular i have this return statement. I am returning a collection of products.
return Product::where('category_id', $category->id)
->select(array('id', 'name', 'newprice', 'image',
'store_id', 'link'))
->paginate(20);
My problem is that instead of returning the store_id(from the products table) i want to return the store name, which is NOT stored in the same table. Is there any way to do that?
Is my only option the use of JOIN?
I recommend you do it with Laravel(/Eloquent) relationships like this:
ApiController:
$productFieldArray = ['id', 'name', 'newprice', 'image', 'store_id', 'link'];
$product = Product::where('category_id', $category->id)
->select(productFieldArray)
->paginate(20);
// Get the value you want (I guess it's the name attribute?)
$store = $product->store->name;
Model relationships:
Product.php:
...
public function store() {
return $this->belongsTo('App\Store');
}
...
Store.php:
...
public function products() {
return $this->hasMany('App\Product');
}
...
Edit:
You have to use the with() function in your query to point out your want to receive the store data too. See: Laravel - accessing relationship Models with AngularJS or http://laravel.com/docs/4.2/eloquent#eager-loading.
So you query will be:
Product::with('store')
->where('category_id', $category->id)
->select(productFieldArray)
->paginate(20);

Resources