Laravel: using "morphMany" and joining another table? - laravel

I'm new to laravel too. I have a question about Eloquent:
table structures:
User: id, username
Posts: id, user_id, content
Likes: id, likeable_type, likeable_id, user_id
(Because of I want to make 'Likes' table extendable for comments, too.)
Models\Likes.php
class Likes extends Model
public function likeable()
{
return $this->morphTo();
}
Models\Post.php
class Posts extends Model
public function likes()
{
return $this->morphMany("\App\Models\Likes","likeable");
}
And when I call
$post = Posts::find($id)->likes()->get();
in my controllers, it will return like this:
"likes": [
{
"id": 1,
"likeable_type": "App\\Models\\Posts",
"likeable_id": 1,
"user_id": 1,
},
{
"id": 2,
"likeable_type": "App\\Models\\Posts",
"likeable_id": 1,
"user_id": 1,
}]
But how can I get results like this:
"likes": [
{
"id": 1,
"likeable_type": "App\\Models\\Posts",
"likeable_id": 1,
"user_id": 1,
"username":"chenhui",//join user on user.id = likes.user_id
}...
Many thanks and sorry for my poor english!

I believe in your Likes model you need
class Likes extends Model
public function user()
{
return $this->belongsTo('App\Models\User');
}
and then it should be:
$post = Posts::find($id)->likes()->with('user')->get();

Related

how to we can get records from two table using Relationships in laravel?

I want that news should also come with all given reviews on it.Help will be highly appriciated.
This is News Model
<?php
namespace Modules\Newsletter\Entities;
use Brexis\LaravelWorkflow\Traits\WorkflowTrait;
use Illuminate\Database\Eloquent\Model;
class News extends Model {
use WorkflowTrait;
protected $table = 'news_info';
protected $fillable = [
'title', 'header', 'description', 'status', 'created_by', 'media_url', 'media_thumbnail',
];
public function reviews() {
return $this->hasMany(NewsReview::class,'reviewable_id');
}
}
This is NewsReview Model
<?php
namespace Modules\Newsletter\Entities;
use App\User;
//use Hyn\Tenancy\Abstracts\TenantModel as TenancyModel;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class NewsReview extends Model {
use SoftDeletes;
protected $fillable = [
'review_text',
'review_reaction',
'is_visible',
'reviewed_by',
'reviewable_id',
'reviewable_type'
];
public function reviewable() {
return $this->morphTo();
}
public function reviewer() {
return $this->hasOne(User::class, 'id', 'reviewed_by');
}
}
This is my controller function
public function newsReview($news){
$review = News::find($news);
$newsReview=$review->reviews;
return $newsReview;
This is the output I am getting by this
{
"id": 1,
"review_text": null,
"review_reaction": "hiie this is revieew",
"is_visible": 1,
"reviewed_by": 1,
"reviewable_id": 1,
"reviewable_type": "Modules\\Newsletter\\Entities\\News",
"created_at": "2020-07-22 14:50:39",
"updated_at": "2020-07-22 14:50:39",
"deleted_at": null
},
{
"id": 2,
"review_text": null,
"review_reaction": "hiie this is revieew 2",
"is_visible": 1,
"reviewed_by": 1,
"reviewable_id": 1,
"reviewable_type": "Modules\\Newsletter\\Entities\\News",
"created_at": "2020-07-22 15:04:25",
"updated_at": "2020-07-22 15:04:25",
"deleted_at": null
},
{
"id": 3,
"review_text": null,
"review_reaction": "hiie this is revieew 3",
"is_visible": 1,
"reviewed_by": 1,
"reviewable_id": 1,
"reviewable_type": "Modules\\Newsletter\\Entities\\News",
"created_at": "2020-07-22 15:04:35",
"updated_at": "2020-07-22 15:04:35",
"deleted_at": null
},
I want that news should also come with all given reviews on it.Help will be highly appriciated.
You can eager load the reviews relationship when retrieving the news model from the database:
public function newsReview($news)
{
return News::with('reviews')->find($news);
}
this will return the news model and all of its reviews nested in the relations property.
From the docs:
When accessing Eloquent relationships as properties, the relationship
data is "lazy loaded". This means the relationship data is not
actually loaded until you first access the property. However, Eloquent
can "eager load" relationships at the time you query the parent model.
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
$books = App\Book::with('author')->get();
you can use laravel eager loading and inside the News model use the proerty $with, which will tell laravel to fetch all reviews associated with a news instance, like so
class News extends Model {
use WorkflowTrait;
protected $table = 'news_info';
protected $fillable = [
'title', 'header', 'description', 'status', 'created_by', 'media_url', 'media_thumbnail',
];
protected $with= ['review']; <-----------
public function reviews() {
return $this->hasMany(NewsReview::class,'reviewable_id');
}
}
this way, whenever you fetch a News model, its reviews will be fetched too!, however if you dont want every query to have the reviews, only specific queries, then you can do this
News::with(['review'])->get()
Can you change the NewsReview table's reviewer relation function with
public function reviewer(){
return $this->belongsTo(User::class);
}
thanks

Get specific field from one to many table relationship with laravel eloquent?

I have table like this following image:
so, user have many balance_transactions and last inserted balance_transactions record will be user account balance. my question, how to make user model have property account_balance with value last inserted total in balance_transactions table
I was tried using something like this in user model
public function balance {
return $this->hasMany(App\BalanceTransaction::class);
}
public function account_balance {
return $this->balance()->orderBy('id', 'DESC')->limit(1);
}
And I get the data like this
$user = User::where('id', 1)->with('account_balance')->first();
return response()->json($user);
and the result look like this folowing json:
{
"id": 1,
"username": "john0131",
"full_name": "john doe",
"email": john#test.com,
"account_balance": [
{
"id": 234,
"user_id": 1,
"total": 7850
"added_value": 50
"created_date": "2020-02-28 12:18:18"
}
]
}
but what I want, the return should be like this following json:
{
"id": 1,
"username": "john0131",
"full_name": "john doe",
"email": "john#test.com",
"account_balance": 7850
}
my question, how to make something like that in laravel eloquent proper way? so I can get account_balance data only with simple code like $user = user::find(1);.
Thanks in advance
One way to tackle this is, you could use laravel Accessors
So in your User model you can create a method as follows
/**
* Get the user's balance.
*
* #return float
*/
public function getAccountBalanceAttribute()
{
return $this->balance->last()->total;
}
then wherever you want to use it you can use it by:
$user->account_balance;
I would suggest loading only one row from your transaction table with an eye on performance. Additionally you can append the value of the accessor to the serialised output of the model (e.g. __toArray()) and return the acutal value only if the relationship has already been loaded.
class User extends Authenticatable
{
protected $appends = ['account_balance'];
public function balance()
{
return $this->hasMany(App\BalanceTransaction::class);
}
public function latestBalance()
{
return $this
->hasOne(App\BalanceTransaction::class)
->latest();
}
public function getAcountBalanceAttribute()
{
if (! $this->relationLoaded('latestBalance')) {
return null;
}
return $this->latestBalance->total;
}
}

Laravel querying polymorphic relationships with nested many to many items

I have 4 tables with the following relationships:
class Note extends Model
{
public function noteable() {
return $this->morphTo();
}
}
class Expense extends Model
{
public function notes()
{
return $this->morphMany(Note::class, 'noteable');
}
}
class Review extends Model
{
public function notes()
{
return $this->morphMany(Note::class, 'noteable');
}
public function repairs()
{
return $this->belongsToMany(Repair::class);
}
}
class Repair extends Model
{
public function reviews()
{
return $this->belongsToMany(Review::class);
}
}
as you can see Note is in the relationship one to many polymorphic and Reviews is in many to many with Repairs.
I would like to take all Notes that Reviews also has Repairs. How do I do such an operation?
Following the documentation I am trying to do something like this:
$notes = App\Note::query()
->with(['noteable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
Review::class => ['repairs']
]);
}])->get();
I would like to my query should return something like:
[
{
"id": 11,
"noteable_id": 4,
"noteable_type": "App\\Expense",
"noteable": {
"id": 4,
"name": "Expense",
"category": "general"
}
},
{
"id": 13,
"noteable_id": 5,
"noteable_type": "App\\Review",
"noteable": {
"id": 5,
"name": "Review 5",
"mileage": 120000,
"repairs": [..., ...] //what I need
}
}
]
To load your Repair models for your Reviews, try this:
if ($type == 'App\Review') {
$query->with('repairs')
}
EDIT :
I've just done some more reading on whereHasMorph and I think i was mistaken as to what it is doing. It is at it's most basic level a where clause, designed to constrain the results of the query. What I have suggested above would the the equivalent of joining something to a MySQL subquery. Not what we want!
What you want to do is actually under
Nested Eager Loading morphTo Relationships
(https://laravel.com/docs/6.x/eloquent-relationships#querying-polymorphic-relationships)

How to attach last records in relation with eloquent in Laravel

I have a group table, messages table, and users table.
The messages table belongs to one user and morphTo a group.
the groups table belongs to one user and morphMany messages.
I want to get all groups related to one user with the last messages "only the last message".
`users Model`
public function messages(){
return $this->hasMany('App\Message');
}
public function groups(){
return $this->belongsToMany('App\Group', 'group_user');
}
`Groups Model`
public function user(){
return $this->belongsTo('App\User');
}
public function messages(){
return $this->morphMany('App\Message', 'messagable');
}
`messages model`
public function user(){
return $this->belongsTo('App\User');
}
public function messagable(){
return $this->morphTo();
}
I want the result to be like that:
{
"id": 23,
"user_id": 2,
"name": "Group1",
"description": "5656",
"img": "images/1524958644.png",
"created_at": "2018-04-28 23:37:24",
"updated_at": "2018-04-28 23:37:24",
"last_messages":{
"id": 292,
"user_id": 1,
"messagable_type": "App\\Group",
"messagable_id": 23,
"message": "hi",
"created_at": "2018-04-29 07:48:55",
"updated_at": "2018-04-29 07:48:55"
}
}
I would use an accessor for this.
eg. In your Groups model
protected $appends = ['last_message'];
public function getLastMessageAttribute()
{
return $this->messages->orderBy('id', 'desc')->first();
}
You could also use orderBy with created_at instead of id
You can then retrieve it by using this as an example:
Group::where('user_id',2)->first()->last_message;

Laravel eloquent relationships, 3 way join

I have 3 tables (simplified here)
users
id, name, email
teams
id, name
team_user
team_id, user_id
I want to send an API query to return all teams a user Id belongs to, and what other members are also in that team. Rather than returning just userIds, I want to fill the arrays with their actual user data, e.g name and email.
Route::get('/user/{id}/teams/', 'UserController#getTeams');
User.php (model)
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
public function teams()
{
return $this->belongsToMany('App\Team', 'team_user', 'team_id', 'user_id');
}
}
Team.php (model)
class Team extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'team_user', 'team_id', 'user_id');
}
}
TeamUser.php (model)
class TeamMember extends Model
{
public function user()
{
return $this->hasOne('App\User');
}
public function team()
{
return $this->hasOne('App\Team');
}
}
UserController.php
class UserController extends Controller
{
public function getTeams($userId) {
return User::find($teamId)->teams()->get();
}
}
This returns:
[
{
"id": 6,
"name": "P12",
"location": "Newbury",
"owner": 6,
"active": 1,
"created_at": "2017-12-20 10:18:00",
"updated_at": "2017-12-20 10:18:00",
"pivot": {
"team_id": 6,
"user_id": 6
}
},
{
"id": 4,
"name": "fwffewfffffff",
"location": "ffffffff",
"owner": 4,
"active": 1,
"created_at": "2017-12-19 19:56:27",
"updated_at": "2017-12-19 19:56:27",
"pivot": {
"team_id": 6,
"user_id": 4
}
}
]
However I would like to also include a list of other users in each of those 2 teams. With their names and emails (from the users table), not just the user_ids.
Is this possible without doing further seperate queries?
You would be able to eagerly load the relations and attach them to the model.
By using the dot notation teams.users, the output will include all users of all teams attached to user with id $userId.
I've added the findOrFail to make sure it will return a 404 when user can not be found.
class UserController extends Controller
{
public function getTeams($userId)
{
return User::with('teams.users')->findOrFail($userId);
}
}
This will return the User and attach the relations.
If you want to just return the teams, you can do something like this:
class UserController extends Controller
{
public function getTeams($userId)
{
$user = User::with('teams.users')->findOrFail($userId);
// $user->teams will hold a collection of teams with their users
return $user->teams;
}
}
Try using with() to retrieve the fields from different tables, for example
class UserController extends Controller {
public function getTeams($userId) {
return User::find($userId)->with('teams')->get();
// return User::find($userId)->teams()->get();
}
}
If you would like to select specific column from the team_members database, you could add function inside with, for example
class UserController extends Controller {
public function getTeams($userId) {
return User::find($userId)->with(['teams' => function($query) {
$query->select('id', 'name');
}])->get();
}
}
For now I have solved this with
class UserController extends Controller
{
public function getTeams($userId) {
$teamWithMembers = [];
$teams = User::find($userId)->teams()->get();
foreach($teams as $team) {
$team->members = Team::find($team->id)->users()->get();
$teamWithMembers[] = $team;
}
return $teamWithMembers;
}
}

Resources