Let us say my shop model has a products relationship as follows:
// shop model class
public function products()
{
return $this->hasMany(Model\Product::class)
->orderBy('name');
}
Is there a way that client code, such as my controller, can remove the orderBy() clause?
Yes there is a way to do that, say in your controller you want to fetch some of the products
\App\Products::latest()->getQuery();
getQuery() is a query builder method that contains all the groupings, selects, orders, wheres, joins etc for the query that you are accessing or trying to build.
So you could reset the ordering like so:
\App\Products::latest()->getQuery()->orders= [];
since in Laravel eloquent query builder selects, grouping, ordering are stored as arrays of key values you simply set that to an empty array to reset al the previous states.
For example grouping:
\App\Products::latest()->getQuery()->groupings =[];
For reset
$qry->getQuery()->groups = [];
$qry->getQuery()->wheres = [];
For remove last where or group by column
array_pop($qry->getQuery()->groups);
array_pop($qry->getQuery()->wheres);
public function products($isOrder = true)
{
$hasMany = $this->hasMany(Model\Product::class);
return ($idOrder) ? $hasMany->orderBy('name') : $hasMany;
}
but you can use this only having $shop instance;
$shop->products(false)->...; // your query to get result
Related
How do I select the column Field from the FormFactor model? I want to select only one column from the recordset.
public function ApiFormFactorDetails()
{
return $this->belongsTo('App\Model\FormFactor','formfactor_id')->select('name');
}
Eloquent models share pretty much the same syntax as the query builder. From the query builder documentation:
If you don't even need an entire row, you may extract a single value from a record using the value method. This method will return the value of the column directly:
$email = DB::table('users')->where('name', 'John')->value('email');
In your case:
public function ApiFormFactorDetails()
{
return $this->belongsTo('App\Model\FormFactor','formfactor_id')->value('name');
}
Actually, the way you are doing should work fine. You can keep the code in the function:
public function ApiFormFactorDetails()
{
return $this->belongsTo('App\Model\FormFactor','formfactor_id')->select('name');
}
And then, call it like this:
$object->ApiFormFactorDetails;
The return will have only name as attribute.
I have following code:
class Ingredient extends Eloquent
{
public function units()
{
return $this->hasMany('IngredientUnit')
->orWhere('ingredient_id', '=', -1);
}
}
I would expect query like:
select * from `ingredient_units` where `ingredient_id` = '-1' OR `ingredient_units`.`ingredient_id` in (...)
instead I get:
select * from `ingredient_units` where `ingredient_id` = '-1' and `ingredient_units`.`ingredient_id` in (...)
Why it use AND operator instead OR, when I used orWhere()?
Update 1:
And second question is how can I get a query which I was expected?
Update 2:
I want to use eagerloading for that
When you fetch a collection of objects through a relation on a model, the relation constraint is always included, hence the AND. And it makes perfect sense, otherwise you could get $model->units objects that are not related to $model.
I can see what you're trying to achieve here - fetch units related to that $model together with units not related to any models. You can achieve it by adding the following method to your model:
public function getAllUnits() {
$genericUnits = IngredientUnit::whereIngredientId(-1);
return $this->units()->union($genericUnits)->get();
}
OR
public function getAllUnits() {
return IngredientUnit::whereIn('ingredient_id', [$this->id, -1])->get();
}
The only issue here is that it won't be used by eager loading logic but would result in separate query for every model for which you want to return units. But if you always fetch that for a single model anyway, it won't be a problem.
One suggestion: store NULL in ingredient_id instead of -1. This way you'll be able to make use of foreign key constraints.
I have set up two Eloquent models with a belongsToMany relation in both directions. This works perfectly fine but now I need to make a more detailed query within the relationship. To keep things simple, let's say the tables have the following columns:
wigs:
- id
- name
- type
heads:
- id
- name
heads_wigs:
- head_id
- wig_id
Now I need to fetch a series of wigs with a given type within a list of given head id's. So what I have is:
a wig type
an array with head id's
I am using Eloquent outside of laravel so I want to start building the ORM query on the model. Something like:
Wig::where( 'type', $type )-> ... //here the code to make the join on head id's
This is where my understanding of SQL lacks but I suppose this should not be too hard to achieve.
UPDATE:
To rephrase it in a sentence: get all wigs with type=wig_type that have a belongsToMany relationship with the heads [1,2,3,5,6,8,9]. I want to end up with a collection of wigs by performing a single query.
You could do something like this
Head::whereIn('id', $head_id_array)->load(['wig' => function($query) use ($wig_type) {
$query->where('type', $wig_type);
}])->get();
or
Wig::where('type', $wig_type)->load(['heads' => function($query) use ($head_id_array) {
$query->whereIn('id', $head_id_array);
}])->get();
if I understand your question correctly.
Or
$wig = Wig::where('type', $wig_type)->get();
$heads = $wig->heads()->whereIn('id', $head_id_array)->get();
$matching_head_ids = $heads->lists('id');
$wig->matching_head_ids = $matching_head_ids;
That way, the wig object returned will have an array of matching head ids.
you could put this in a method on your wig model:
class Wig extends Eloquent {
public function heads()
{
return $this->belongsToMany('Head');
}
/**
* #param array $head_ids Provided head id array
* #return array Array of this wigs head id's which match the provided head ids
*/
public function matchingHeadIds($head_ids)
{
return $this->heads()->whereIn('id', $head_ids)->get()->lists('id');
}
}
then use it like so
$wig = Wig::where('type', $wig_type);
$wig->matchingHeadIds($head_ids);
Edit
This is not a simple task for an ORM like eloquent, since it treats each record like a row from the table, so something like this wouldn't work:
$wigs = Head::whereIn('id', $head_ids)->wigs()->where('type', $wig_type)->get();
There is a whereHas method available which you can use like so:
Wig::where('type', $wig_type)->whereHas('heads', function ($query) use ($head_ids) {
$query->whereIn('id', $head_ids);
})->get();
which should give you the results you need.
I have a many to many relationship for orders and products.
<?php
class Order extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
public function products()
{
return $this->belongsToMany('Product');
}
}
?>
<?php
class Product extends Eloquent {
public function orders()
{
return $this->belongsToMany('Order');
}
}
?>
Need to fetch the number of times each product is ordered.In mysql,this task can be achieved by using the following query
SELECT products.id, products.description, count( products.id )
FROM products
INNER JOIN order_product ON products.id = order_product.product_id
INNER JOIN orders ON orders.id = order_product.order_id
GROUP BY product_id
LIMIT 0 , 30
Result of the above query is as follows:-
id description count(products.id)
1 Shoes 3
2 Bag 2
3 Sun glasses 2
4 Shirt 2
How this task can be achieved using laravel eloquent (without using query builder)????How can i fetch the number of times each product is ordered using laravel eloquent??
For future viewers, as of Laravel 5.2, there is native functionality for counting relationships without loading them, without involving your resource model or accessors -
In the context of the example in the approved answer, you would place in your controller:
$products = Product::withCount('orders')->get();
Now, when you iterate through $products on your view, there is a orders_count (or, generically, just a {resource}_count) column on each retrieved product record, which you can simply display as you would any other column value:
#foreach($products as $product)
{{ $product->orders_count }}
#endforeach
This method produces 2 fewer database queries than the approved method for the same result, and the only model involvement is ensuring your relationships are set up correctly. If you're using L5.2+ at this point, I would use this solution instead.
Mind that Eloquent uses Query\Builder under the hood, so there is no such thing in Laravel, like 'query eloquent without using query builder'.
And this is what you need:
// additional helper relation for the count
public function ordersCount()
{
return $this->belongsToMany('Order')
->selectRaw('count(orders.id) as aggregate')
->groupBy('pivot_product_id');
}
// accessor for easier fetching the count
public function getOrdersCountAttribute()
{
if ( ! array_key_exists('ordersCount', $this->relations)) $this->load('ordersCount');
$related = $this->getRelation('ordersCount')->first();
return ($related) ? $related->aggregate : 0;
}
This will let you take advantage of eager loading:
$products = Product::with('ordersCount')->get();
// then for each product you can call it like this
$products->first()->ordersCount; // thanks to the accessor
Read more about Eloquent accessors & mutators,
and about dynamic properties, of which behaviour the above accessor mimics.
Of course you could use simple joins to get exactly the same query like in you example.
If you already have the $products object, you can do the following:
$rolecount = $products->roles()->count();
Or if you are using eager loading:
$rolecount = $products->roles->count();
Cheers.
I am using Laravel 5.1 and i am able to accomplish that by doing this
$photo->posts->count()
And the posts method in Photo model looks like this
public function posts(){
return $this->belongsToMany('App\Models\Posts\Post', 'post_photos');
}
I'm trying to improve some code in my app in laravel 4 and trying to implement relationships between models.
I have a table called reservations which has a one to many relationship with items. Items has a one to one relationship with Products. Basically as a reservation the items that are included in the reservation are added to the items table. The specs of the items comes from the products table (type, value etc)
I've set up the relationships in the models as follows:
in the reservations Class:
public function item() {
return $this->hasMany('Item');
}
in the items Class:
public function reservation() {
return $this->belongsTo('Reservation');
}
public function product() {
return $this->hasOne('Product');
}
in the product Class:
public function item() {
return $this->belongsTo('item');
}
I'm trying to now query the reservations for a calendar view. I'm retrieving all the reservations in a month using the following:
$events = Reservation::where('play_date','>=',$start->format('Y-m-d'))
->where('play_date','<=', $end->format('Y-m-d'))
->get();
I'm then trying to iterate through the collection (is it a collection or a result set?) using the following:
$events->each(function($event) { }
I then want to iterate through all the items of the reservation and it's this bit that's confusing me.
$items = $event->item()->get();
which does create an object I can then iterate through this sub collection using another call back but I'm then struggling to get the product information using:
$item->product()->type
I get an error:
Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$type
How can I iterate through the items correctly using laravel relationships? What is the best practice in this and retrieve details relating to the item from the product table
Thank you
The main point to do what you want is understand the difference between $event->item and $event->item(). Basically, $event->item is the same as $event->item()->get(). Knowing this, you should be doing something like this
$events = Reservation::where('play_date', '>=', $start->format('Y-m-d'))
->where('play_date', '<=', $end->format('Y-m-d'))
->get();
$events->each(function ($event) {
$items = $event->item;
$items->each(function ($item) {
$type = $item->product->type;
});
});
You may also want to take a look at Eager Loading your relationships, as to reduce the number of queries ran.