Laravel 5.4 orderBy on morphTo relation - php

I am trying to order the results of a morphTo relation in Laravel 5.4. The below example does not work. Adding an orderBy method on other relations ( eg hasMany() ) does work.
class OrderLineItem extends Model
{
public function eventtable()
{
return $this->morphTo()->orderBy('date');
}
}
I have been able to order the result set after the query, by using sortBy on the collection. But would be good to order the results in the query. The date col is always available in the polymorphic related tables.

class OrderLineItem extends Model
{
public function eventtable()
{
return $this->morphTo()->orderBy('date','Desc');
//or you can use
//return $this->morphTo()->latest('date');
}
}

Related

how to get a belongsToMany() query from a collection ? MySQL/Laravel

I'm using Laravel 5.4.22 (the newest one). In MySQL, I have two tables, tag_categories and tags, which form a many-to-many relationship. What I need is a query which returns all the tags for the selected categories. I know how to solve this when I have only one object, and I know how to solve this with querying and looping each of those objects, but there has to be a query or eloquent based solution for the whole thing?
I understand the code below doesn't work because I'm using ->belongsToMany on a collection rather than an object, but how to I bridge this gap the simplest way?
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)
->belongsToMany(Tag::Class)->get();
dd($resultingTags);
belongsToMany generally belongs in the model class, not a method called on the fly. When looking to eager load the relationship, you then call the with() method on the query builder.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
ex:
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
// Query
$users = User::with('roles')->get();
$rolesOfFirstUser = $users->first()->roles;
If you're trying to get all the tags of the given categories, then you should be querying tags, not tag_categories.
Tag::whereHas('categories', function ($query) use ($chosenCategoriesIds) {
$query->whereIn('id', $chosenCategoriesIds);
})->get();
This is One-to-many relation
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->hasMany('App\Tag');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
If you want define Many-To-Many relation for this case
You need to have 3 tables tags, tag_categories, tag_tag_category
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->belongsToMany('App\Tag', 'tag_tag_category', 'tagcategory_id', 'tag_id');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();

Laravel 5.3 Polymorphic many to many - how to get all the related rows regardless of their table sorted by pivot column?

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!

Laravel - eager loading a method (not a relationship) of an Eloquent model

Like we can eager load a relationship of an Eloquent model, is there any way to eager load a method which is not a relationship method of the Eloquent model?
For example, I have an Eloquent model GradeReport and it has the following method:
public function totalScore()
{
return $scores = DB::table('grade_report_scores')->where('grade_report_id', $this->id)->sum('score');
}
Now I am getting a collection of GradeReport Eloquent models.
$gradeReports = GradeReport::where('student_id', $studentId)->get();
How can I eager load the returning values of totalScore method for all GradeReport Eloquent models in the collection?
You can add arbitrary properties to your models by adding them to $appends array and providing a getter. In your case the following should do the trick:
class GradeReport extends Model {
protected $appends = ['totalScore'];
public function getTotalScoreAttribute() {
return $scores = DB::table('grade_report_scores')->where('grade_report_id', $this->id)->sum('score');
}
}
Now all GradeReport objects returned from your controllers will have totalScore attribute set.
The table grade_report_scores would also have an eloquent model xyz on which you define a relationship, query scopes, functions, etc.
Add relationship into GradeReport model:
public function scores()
{
return $this->hasMany('xyz', 'grade_report_id', 'id');
}
Now you can rewrite your totalScore function into this:
public function totalScores()
{
return $this->with(['scores' => function ($query) {
$query->sum('score');
}])->get();
}
not tested, note the closure, there bight be a need to call $query->select('id', 'srore', 'other used values');

Laravel Eloquent Joins

I have below query in core php:
SELECT DISTINCT device_tocken FROM push_details JOIN users ON users.id=push_details.user_id
I have to integrate it in laravel 4
Application already have User extends Eloquent class
I created Push_details class as below
class Push_details extends Eloquent {
public $table = 'push_details';
public function User() {
return $this->hasMany('\User','id');
}
}
Table : users
Primary key : id
Table: push_details
Primary key: id
Foreign key: user_id belongsTo('users.id');
But i m not able to get expected result.
One more thing i didn't write anything in User's model yet.
Only way to join table is.. to join it, as Eloquent relations don't work using joins but separate queries with WHERE IN clauses. So this will do:
DB::table('push_details')
->select('device_tocken')
->distinct()
->join('users','users.id','=','push_details.user_id')
->get();
Above will return array of stdObject's so or if you need Eloquent Collection with Eloquent models as a result replace DB::table('push_details')->select... with PushDetails::select...
Now, correct your relations, as they are wrong:
// PushDetails model (as previously stated, I suggest renaming it to StudlyCase)
public function user() {
return $this->belongsTo('\User','user_id'); // user_id is may be omitted here
}
// User model
public function pushDetails() {
return $this->hasMany('\PushDetails','user_id'); // user_id is may be omitted here as well
}
In your User model, you need to link back to the PushDetails model, like so
class User extends Eloquent {
public function push_details() {
return $this->belongsTo('PushDetails');
}
}
Use CamelCase for Class names, because laravel has several functions, in which CamelCase are changed to snake_case
Change
public function User() {
return $this->hasMany('\User','id');
}
to
public function users() {
return $this->hasMany('User');
}
See the docs 'Eloquent ORM' for more...

Laravel attach() method not working to hasMany side

The application has the models:
Atividade.php
class Atividade extends Eloquent {
public function intervencoes() {
return $this->belongsToMany('Intervencao');
}
}
Intervencao.php
class Intervencao extends Eloquent {
public function atividades() {
return $this->hasMany('Atividade');
}
}
The following code works:
Atividade::find($id)->intervencoes()->attach($intervencao_id);
But, this...
Intervencao::find($id)->atividades()->attach($atividade_id);
Returns an BadMethodCallException:
Call to undefined method Illuminate\Database\Query\Builder::attach()
SOLUTION (thanks to #gnack):
I was trying to set a many-to-many relationship, so just needed to change this...
return $this->hasMany('Atividade');
To this:
return $this->belongsToMany('Atividade');
See the Laravel documentation here:
http://laravel.com/docs/eloquent#inserting-related-models
Basically you have set up two different types of relationships for the same two tables - you've set up a many-to-many and a one-to-many. It looks as though you probably wanted a many-to-many, so you'll need to change this line:
return $this->hasMany('Atividade');
To this:
return $this->belongsToMany('Atividade');
This will set the relationship up as a many-to-many relationship, which will then support the attach() method.
The attach() method is only for many-to-many, for other relationships there's save() or saveMany() and associate() (see the docs linked above).
See the documentation Laravel 5.7
A Comment belongTo an unique Post
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}
A Post can Have multiple Comments
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
When you want to update/delete a belongsTo relationship, you may use the associate/dissociate method.
$post= App\Post::find(10);
$comment= App\Comment::find(3);
$comment->post()->associate($post); //update the model
$comment->save(); //you have to call save() method
//delete operation
$comment->post()->dissociate();
$comment->save(); //save() method
attach() is for many-to-many relationships. It seems your relationship is supposed to be a many-to-many but you have not set it up correctly for that.
class Intervencao extends Eloquent {
public function atividades() {
return $this->belongsToMany('Atividade');
}
}
Then the attach() should work
In my case I've two roles() methods that's why it's throwing this error.

Categories