I have 2 collections.
Vehicles and Views.
I would like to bring back a list of vehicles sorted by the number of views.
My Vehicle class
class Vehicle extends Moloquent {
protected $dates = ['date_assigned'];
public function associated_views()
{
return $this->hasMany('App\Collections\View');
}
}
And my View class
class View extends Moloquent {
public function associated_vehicle()
{
return $this->belongsTo('App\Collections\Vehicle');
}
}
I can get the count of the views after the fact, with $vehicle->associated_views->count(), but this doesn't enable me to sort on the field before pulling back every single record. Is this possible?
Use withCount():
Vehicle::withCount('views')->latest('views_count')->get()
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models
https://laravel.com/docs/5.5/eloquent-relationships#counting-related-models
Related
I have three models: ProductType, ProductSubtype, and ProductSubtypeCategory
ProductType.php
class ProductType extends Model{
// A product type has many subtypes
public function product_subtypes(){
return $this->hasMany(ProductSubtype::class);
}
}
ProductSubtype.php
class ProductSubtype extends Model{
// Each product subtype belongs to a type
public function product_type(){
return $this->belongsTo(ProductType::class);
}
// A product subtype has many categories
public function product_subtype_categories(){
return $this->hasMany(ProductSubtypeCategory::class);
}
}
ProductSubtypeCategory.php
class ProductSubtypeCategory extends Model{
// Each cateogory belongs to a subtype
public function product_subtype(){
return $this->belongsTo(ProductSubtype::class);
}
}
I only want product types where product subtype(s) and subtype category(s) within that subtype exist. So far I have tried this
return ProductType::has('product_subtypes', function ($query){
$query->has('product_subtype_categories');
})->get();
Is there any official way to get the results I want from such nested relations?
What you are doing is correct, but can be simplified.
Change the following:
return ProductType::has('product_subtypes', function ($query){
$query->has('product_subtype_categories');
})->get();
to:
return ProductType::has('product_subtypes.product_subtype_categories')->get();
From the docs:
When accessing the records for a model, you may wish to limit your
results based on the existence of a relationship. For example, imagine
you want to retrieve all blog posts that have at least one comment. To
do so, you may pass the name of the relationship to the has and orHas
methods:
// Retrieve all posts that have at least one comment...
$posts = App\Post::has('comments')->get();
Nested has statements may also be constructed using "dot" notation.
For example, you may retrieve all posts that have at least one comment
and vote:
// Retrieve posts that have at least one comment with votes...
$posts = App\Post::has('comments.votes')->get();
I currently have an Eloquent Model that I have tried to simplify for this example and has a similar structure to the below.
class Student extends Model {
public function classes()
{
return $this->hasMany('App\Classes', 'class_code','code');
}
public function events()
{
return $this->hasOne('App\Events', 'event_code', 'code');
}
}
Every student has an assigned code. Hence a student can be matched to classes or event in a one to many / one relationships via this code. The issue is the relationship of the event. The code is slightly different.
In classes, the code will be 11-ABCD.00
For events, the code is: 11-ABCD
The decimal point is missing in the event code but otherwise, the code is the same. The decimal point simply allows for finer sub-divisions. For relationships, it does not matter and may not always exist i.e. A student may not have a class or event related to them.
I can manually retrieve an Event record like this:
class Student extends Model {
public function events($code)
{
$code = explode('.', $code);
if(count($code) > 0)
{
$code = $code[0];
}
return Event::where('code', $code)->first();
}
}
But this isn't in the true spirit of eloquent when I want to retrieve an entire collection e.g.
$results = Student::with('events')->first();
In short, can I design the relationship of the event to automatically take the key 'code' and strip it so that I can retrieve the records that are relevant?
Example Coding:
Student (Model) (10-ABCD.10)
Classes (One to Many) (10-ABCD.10)
Event (One to One) (10-ABCD)
I had an accessor set on my Eloquent model that worked fine, but the associated database query was getting run once for every instance of the model I created. On my index page this meant 5 dozen queries.
<?php
class Thingy extends Model {
protected $appends = ["parentType"];
public function getParentTypeAttribute($value) {
return self::where("type"=>$this->type, "parent"=>1)->value("name");
}
}
class ThingyController extends Controller {
public function index() {
$thingys = Thingy::all();
return view("things.index", compact("thingys"));
}
}
To explain briefly: there are two classes of "thingy" in the same database table, the class being indicated by a boolean value named "parent." I want to get the name of the parent when I access the child. I know this should be two tables but it's not.
I wanted to reduce the number of database reads, so I tried changing it to a relationship instead. I figured this way I could take advantage of eager loading.
<?php
class Thingy extends Model {
public function parent() {
return $this->hasOne("Thingy", "id")->where("type"=>$this->type, "parent"=>1);
}
}
class ThingyController extends Controller {
public function index() {
$thingys = Thingy::with(["parent"]);
return view("things.index", compact("thingys"));
}
}
The problem is that within the relationship method, $this is an empty instance of the model, unlike in the accessor, so $this->type is null.
Is there a way to access properties of the model I'm working with from within a relationship method?
Figured that out. Since I'm essentially doing a self-join on the same table, I can specify the "local" and "foreign" ID columns as the column I'm trying to match:
public function parent() {
return $this->hasOne("Thingy", "type", "type")->where("parent"=>1);
}
I guess the key concept was to remember that I'm defining a relationship between two instances of the model, which is independent of the particular instances I'm dealing with.
I have a model Page and many models called SomethingSection - they're connected through a polymorphic m-m realtionship and the pivot has an additional column 'position'.
I need to write a relationship (or accessor maybe?) on the Page model that will return a collection of all connected Sections, regardless of their model (read: table).
My models:
class Page extends Model {
public function introSections()
{
return $this->morphedByMany(IntroSection::class, 'pagable');
}
public function anotherSections()
{
return $this->morphedByMany(AnotherSection::class, 'pagable');
}
}
class IntroSection extends Model {
public function pages()
{
return $this->morphToMany(Page::class, 'pagable');
}
}
class AnotherSection extends Model {
public function pages()
{
return $this->morphToMany(Page::class, 'pagable');
}
}
The pivot column looks like this:
pagables
-page_id
-pagable_id
-pagable_type
-position
I'm looking for a way to call a method/attribute on the Page model and get all the connected sections in a single collection, sorted too. What would be a good way to go about this?
I understand that the connected sections do not have the same interface, but in my case that's not a problem at all (in terms of what I will do with the data).
I also understand that relationships perform a separate query (for each relationship), so getting all of them with 1 query is impossible (also different interfaces would be a problem here). And for the same reason the sorting will need to be done on the collection level, not in query.
How could I make this as maintainable as possible and preferably with as small a performance hit as possible.
Thanks in advance.
You can use withPivot() method after your relationship to get the pivot columns with relation like this:
class Page extends Model {
public function introSections()
{
return $this->morphedByMany(\HIT\Models\Sections\IntroSection::class, 'pagable')
->withPivot(['position']);
}
public function anotherSections()
{
return $this->morphedByMany(AnotherSection::class, 'pagable');
}
}
class IntroSection extends Model {
public function pages()
{
return $this->morphToMany(Page::class, 'pagable')
->withPivot(['position']);
}
}
and you can use collection's sortBy to sort the collection by using sortBy() method like this:
$sorted_collection = IntroSection::pages->sortBy('pagables.position');
UPDATE:
You can use collection's combine() method to get all the relationships like this, add this method inside your Page Class:
public function getAllSections()
{
return $this->introSections->combine($this->anotherSections-toArray())
->sortBy('pagables.position'):
}
Hope this helps!
I'm using Laravel as a REST API for a SPA. I have a relationship where families have multiple contributions. The contributions table has a foreign key reference to family's id. I can call on the contributions route with the hasMany/belongsTo set up, and every contribution gets the entire family model it belongs to. But I don't need all that data, I just need a single field from the family table (not the id, but a different field) with each contribution.
Here are my models and resource controller:
class Family extends Eloquent {
protected $table = 'families';
// relationships
public function contributions() {
return $this->hasMany('Contribution');
}
}
class Contribution extends Eloquent {
protected $table = 'contributions';
// relationships
public function family() {
return $this->belongsTo('Family');
}
public function other_field() {
return $this->belongsTo('Family')->select('other_field');
}
}
class ContributionController extends BaseController {
public function index()
{
// works - but returns the entire family with every contribution
$contributions = Contribution::with('family')->get();
// returns other_field == null with every contribution
$contributions = Contribution::with('other_field')->get();
return Response::json($contributions->toArray(),
200);
}
Where am I going wrong with selecting this single field from the belongsTo relationship?
You can use query constraints on the relationship if you use eager loading.
Family::with(['contributions', function($query)
{
$query->select('column');
}])->get();