How to load specific fields from a nested relationship with Eloquent? - php

I have an Order model which belongs to a User and has Revisions.
When I try to load orders including: the 'id' and 'name' field from its user and some fields including 'user_id' field from whole revisions, I do:
return Order::with(array('user' => function ($query) {
$query->select('id','name');
}), 'revisions' => function ($query) {
$query->select('id','created_at','user_id','operation','text');
})->get();
However, I'd like to get the name of the user, which naturally is at users table as 'name' field.
Relationships setup
Order
public function user() {
return $this->belongsTo('User');
}
public function revisions() {
return $this->hasMany('Revision');
}
Revision
public function order() {
return $this->belongsTo('Order');
}
User
public function orders() {
return $this->hasMany('Order');
}
public function revisions() {
return $this->hasMany('Revision');
}
Expected result
Just for fast typing reasons I represent it as a JS object ...
orders = [
{ id: 5,
user: {....},
revisions: [
{ id:100,
operation: 'Creation',
text: 'Please provide soon',
user: 'John Doe' // Here I got the name instead of a user_id
}
]
},
{...}
]
How can I complete this?
Note
I did the proper relationship setups at each model.

well, you get user_id in revisions because it is a property of revision itself, you should look for the username in the Order object returned
$x = Order::with(array('user' => function ($query) {
$query->select('id','name');
}), 'revisions' => function ($query) {
$query->select('id','created_at','user_id','operation','text');
})->get();
var_dump($x->user->name);
$x->user is the relation model, user->name is its property
So, to have it also in revision you have to do something like:
$x->each(function(&$order) {
$order->revisions->each(function(&$rev) use ($order) {
$rev->setUsername($order->user->name);
});
});
assuming you have a setUsername method in your relation Model. In any case, the username in relation model should be a private property, not a fillable field, because is not part of the table, and in this case you have to check if it is exported when you do a ->toJson()

Related

Laravel: Seeding Database table with relationship When the table has two foreign key

I am using laravel factory method for populating my database table. I have three models and their relationship with each other are given below:
**User model**
class User extends Authenticatable
{
public function books() {
return $this->hasMany('App\Book', 'user_id', 'id');
}
public function ratings() {
return $this->hasMany('App\Rating', 'user_id', 'id');
}
}
**Book Model**
class Book extends Model
{
public function user() {
return $this->belongsTo('App\User', 'user_id', 'id');
}
public function ratings() {
return $this->hasMany('App\Rating', 'book_id', 'id');
}
**Rating Model**
class Rating extends Model
{
public function book() {
return $this->belongsTo('App\Book', 'book_id', 'id');
}
public function user() {
return $this->belongsTo('App\User', 'user_id', 'id');
}
}
And my factory definition for model Book and Rating look like this:
$factory->define(Book::class, function (Faker $faker) {
return [
'title' => $faker->title,
'description' => $faker->sentence
];
});
$factory->define(Rating::class, function (Faker $faker) {
return [
'rating' => $faker->numberBetween(1,5)
];
});
Database table look like this:
User table:
id,name,email,password
Book table:
id,user_id,title,description
Rating table:
id,user_id,book_id,rating
The code for calling factory is given below:
public function run()
{
//create 10 users
$user = factory(App\User::class,10)->create();
// create 2 books for each user
$user->each(function ($user) {
$book = $user->books()->saveMany(factory(App\Book::class,2)->make());
});
});
So with this run method i am able to create 10 users and each user have two books. But i also want to have each book rated by 3 different users. So what extra code should i add in above method to achieve this.
I have a simple approaching method that you may consider
//create 10 users
$user = factory(App\User::class,10)->create();
$id_arr = Arr::pluck($user , 'id'); // get user id array
// create 2 books for each user
$user->each(function ($user) use($id_arr) {
$books = $user->books()->saveMany(factory(App\Book::class,2)->make());
$shuffle_id = Arr::shuffle($id_arr); // shuffle user_id each time create user successfully
// add 3 rates for each book with different user
$books->each(function ($book) use($shuffle_id) {
$book_id = $book['id'];
for ($i=0; $i<3; $i++){
$user_id = $shuffle_id[$i]; // get id in first 3 of shuffle_id array (this will make sure, user_id will never be duplicated
factory(App\Rate::class)->create(['book_id'=>$book_id, 'user_id' => $user_id]);
}
});
});
Hope it will help

laravel get user id in users table at the time of validation exists|users on call of api?

I have started using laravel and I am loving it.
I write APIs for mobile developers.
I use lot of tables with joins on id_user.
thus I always need id_user.
I have table users which has columns as follows:
1. id
2. token
3. full_name
I do validation as follows:
$result=$request->validate([
'token' => 'required|unique|exists:users'
]
]);
with above code I always get true or false in $result.
but I want the id of this token, which I use as unique in other tables.
thus
I fire the below query as follows:
$users=DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
$id_user=$users->id;
but my question is if in laravel validation, "unique|exists:users" checks for unique record existing in users table, and when it finds the record, it validates this constraint and (returns true), it must be storing this unique record somewhere.
Where can I get this record?
If laravel doesn't have this feature they must add it.
Why should I join user table (or fire another query again?) and make the process expensive?
you need to create models and set relationships (User and Order)
class User extends Authenticatable{
protected $fillable = [
'name', 'email','token', 'password',
];
public function order()
{
return $this->hasMany(Order::class);
}
}
class Order extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
set foreign key on orders table
$table->foreign('user_id')->references('id')->on('users');
then this should be the query
'token' => 'required|unique:users|exists:users'
$order = Order::find(1)->user()->where('token_in_orders_table', 'token_in_users_table')->first();
return $order;
Or create a Resource for the Order Model
class OrderController extends Controller
{
public function index(Request $request)
{
//validate token
'token' => 'required|unique:users|exists:users'
//then
return new OrderResource(Order::find(1));
}
}
Resource
class OrderResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'order_details' => $this->order_details,
'user' => $this->user()->where('token', $request->token)->first(),
];
}
}
enter image description here

How to connect already connected table with other table in laravel?

I have a user table it has one userRole and userRole belongs to Role. So, I want to fetch the userRole and Role also.
Code in user Model:
public function userRole()
{
return $this->hasOne(UserRole::class);
}
Code in UserRole model:
public function role()
{
return $this->belongsTo('App\Role');
}
Code in controller:
User::with('userRole', function ($role) {
$role->with(['Role']);
})
->wherehas('userRole', function ($query) {
$query->where('role_id','1');
})->get();
This is giving me error
"mb_strpos() expects parameter 1 to be string"
The problem is that you should pass an array when you want to add a constrait to the with() method.
Your code should like something like:
User::with([
'userRole' => function ($query) {
...
}
])
...

Laravel Eloquent Foreign Key Relation

I have question about Laravel Eloquent. I created few tables and models, ~like this:
Trip
id
name
user
User
id
email
Message
id
content
trip
user
How can I get all message for single user with all foreign keys? You know, all data from this tables. Like this:
[
1 => [
'content',
'trip' => [
'name'
],
'user' => [
'email'
]
]
]
It's possible, to get in easy way all data?
My models:
// Message.php:
public function user()
{
return $this->belongsTo('App\User');
}
public function trip()
{
return $this->belongsTo('App\Trip');
}
// Trip.php:
public function user()
{
return $this->belongsTo('App\User');
}
// User.php:
public function trips()
{
return $this->hasMany('App\Trip');
}
public function messages()
{
return $this->hasMany('App\Message');
}
My Code
dd(
User::with([
'sent',
'recipient'
])->find(2)->toArray()
);
And what I want to get:
screen
I believe you are looking for the load method.
Lets say you have a user
$user->load('messages'); // var_dump($user);
If you vardump your user object you will see the related messages were loaded.
Use eager loading to achieve that.
Try this:
$users = User::with([
'messages.trip',
])->get();
dd($users);
Ref: https://laravel.com/docs/5.3/eloquent-relationships#eager-loading

use laravel advanced where clauses to run a query

Suppose I have a Course Model like this :
class Course extends Model
{
public function users ()
{
return $this->belongsToMany('App\User', 'course_user', 'course_id', 'user_id');
}
public function lessons ()
{
return $this->hasMany('App\Lesson', 'course_id', 'course_id');
}
}
Course fields are :
course_id
title
Each Course can have multiple lessons.
Lesson Model is like :
class Lesson extends Model
{
public function course ()
{
return $this->belongsTo('App\Course', 'course_id', 'course_id');
}
public function users ()
{
return $this->belongsToMany('App\User', 'lesson_user', 'lesson_id', 'user_id');
}
}
And it's fields are:
lesson_id
title
course_id
As you see there is a OneToMany relation between Course and Lesson and a ManyToMany relation between User and Course.
User And Course Pivot table named ~course_user` have these fields :
course_id
user_id
In the other hand there is a ManyToMany relation between User and Lesson. pivot table for those named lesson_user and have these fields :
lesson_id
user_id
passed
passed field show status of a user in a lesson. if it was 0 ,means user has not passed it yet otherwise he passed it.
User Model is like :
class User extends Model
{
public function lessons()
{
return $this->belongsToMany('App\Lesson', 'lesson_user', 'user_id', 'lesson_id')
}
public function courses ()
{
return $this->belongsToMany('App\Course', 'course_user', 'user_id', 'course_id');
}
}
Now I want to get user courses and calculate percent of passed lessons in each Course via best way, for example nested where clauses.
I think this might be not the best way. But it is easy to understand and maintainable
$courses = $user->courses->map(function($cource){
$all_lessions = $cource->pivot->count();
$done_lessions = $cource->pivot->where(passed,'<>',0)->count();
$percent = $done_lessions * 100 / $all_lessions;
return $cource->push(['percent'=>$percent]);
});
Now you can access through
foreach ($courses as $cource){
$cource->percent;
$cource->title;
//...
}
With inspiration from #KmasterYC answer I wrote bellow codes and all things work:
$userCourses =
$currentUser->courses()
->take(3)
->get();
$userCourses->map(function ($course) use ($currentUser) {
$allLessonsCount = $course->lessons->count();
$courseLessonID = $course->lessons->lists('lesson_id')->toArray();
$userLessonsCount = $currentUser->lessons()
->where('passed', '=', true)
->whereIn('lesson_user.lesson_id', $courseLessonID)
->count();
$percent = round($userLessonsCount * 100 / $allLessonsCount);
$course['percent'] = $percent;
});

Categories