Weird behaviour of Eloquent One To One relationship - laravel

I need to get User with related Profile, but In case below the profile field is null:
Route::get('/user', function (Request $request) {
return $request->user()->load('profile'); // { id: 1, ... profile: null }
});
But in this case the profile field is filled:
Route::get('/user', function (Request $request) {
$user = $request->user();
$profile = $user->profile;
return $user; // { id: 1, ... profile: { name: 'alex', ... } }
});
How can you explain this behavior and what is the correct way to load Profile in my case?
Relations:
public function user(){
return $this->belongsTo('App\Models\User');
}
public function profile(){
return $this->role == 'model' ? $this->hasOne('App\Models\Model\Profile') : $this->hasOne('App\Models\Client\Profile');
}

load() is Lazy Eager Loading
Using load(), you nedd to run the initial query first, and then eager load the relation at some later point. This is "Lazy" eager loading. lazy eager load a relationship after the parent model has already been retrieved.
laravel with() method versus load() method

Go through this for a clear view Eager loading
So to get relationship using with() it will run both queries at same time and will have relation attached to model collection but while using load() first we get the model then on some condition we use load to get relational data. e.g. :
$users = User::all(); //runs first query
if($condition) {
$users = $users->load('organisations'); //lets assume organisations is relation here.
//here runs the 2nd query
}
Hope this helps.

Related

Laravel how to get only relation data

this is my User.php relation code
public function activities()
{
return $this->hasMany(Activities::class, 'builder');
}
and this is my query to get only relation data
return User::whereHas('activities', fn($query) => $query->where('user_id', 1))
->paginate();
but it returns only user data without appling any relation, and its pagination not get to use pluck
i have also tried this
User::where('username', request()->username)->has('activities')->paginate();
but i need only get relation data not user with relation, and i prefer do it with whereHas
You need to create reverse relation for Activities model:
class Activities extends Model
{
// ...
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}
And get all the activities using Activities builder:
$paginated = Acitivities::query()
->whereHas('user', static function(Builder $userQuery) {
$userQuery->where('name', request()->username); // Think about security!
})
->paginate();
Note: All the models must be named in singular (e.g. Activity, User, Product and etc.).

Laravel 5 - Defining Two Relationships Between Models

I'm learning Laravel, and really OOP in general. I've followed several YouTube tutorial series, that teach you to create a blog in Laravel.
I'm building a task app for a brewery, and I'm trying to define the relationships between the users and the tasks. So I have two models: User.php and Task.php. I had no problem defining the user in a hasMany tasks relationship, and reciprocally, a task belongsTo a user. Where I'm confused is that I'd like to also have a user belong to the task as well. I have two MySQL columns, one with the heading of "user_id" and the other with "user_assigned_id". What I want is that a user has many tasks, but a task also has one assigned user, the idea being that the user that created the task might assign the task to another user. I've found several tutorials on creating relationships between three models, such as a user owning several messages, but only having one address, so I figured that I could just treat two models as if they were three models and connected the User model back to the Task model in a hasOne relationship, but I'm having a really hard time passing that through to the Controller and View.
Here is the relevant code in each file:
User.php
public function tasks()
{
return $this->hasMany('App\Task');
}
Task.php
public function user()
{
return $this->belongsTo('App\User');
}
// Added an user_assigned_id relationship
public function user_assigned()
{
return $this->hasOne('App\User', 'name', 'user_assigned_id');
}
DashboardController.php
public function index()
{
$user_id = auth()->user()->id;
$now = Carbon::now();
$tasks_assigned = Task::orderBy('date', 'asc')->whereDate('date', '>=', $now)->where('user_assigned_id', '=', $user_id)->user_assigned()->where('name', '=', 1)->get();
$tasks_created = Task::orderBy('date', 'asc')->whereDate('date', '>=', $now)->where('user_id', '=', $user_id)->get();
return view('dashboard')->with('tasks_assigned', $tasks_assigned)->with('tasks_created', $tasks_created);
}
I've gotten a bit turned around in the Controller, so I'm not sure if I messed something up there. Basically, I'm getting results from tasks owned by the logged in user, but not assigned to the logged in user.
You can just add a second relationship defined on your Task.php Model and assign a different agent based on user_assigned_id. You can manipulate it as expected via Eloquent.
Task.php
public function user() {
return $this->belongsTo('App\User');
}
public function assignedUser() {
return $this->belongsTo('App\User', 'user_assigned_id');
}
Then on DashboardController.php
$tasks_assigned = Task::orderBy('date', 'asc')->whereDate('date', '>=', $now)->where('user_assigned_id', '=', $user_id)->get();
should work
public function user()
{
return $this->belongsTo('App\User');
}
// Added an user_assigned_id relationship
public function assignee()
{
return $this->belongsTo('App\User', 'user_assigned_id');
}
The relationship is still a belongsTo, you just need to provide the column where the foreign key is held.
Other files:
User.php
public function ownedTasks()
{
return $this->hasMany('App\Task');
}
public function assignedTasks()
{
return $this->hasMany('App\Task', 'user_assigned_id');
}
Dashboard Controller
public function index()
{
$now = Carbon::now();
$tasks_assigned = Auth::user()->assignedTasks()->where('date', '>=', $now)->get();
$tasks_created = Auth::user()->ownedTasks()->where('date', '>=', $now)->get();
return view('dashboard')->with(compact('tasks_assigned', 'tasks_created'));
}

Get indirect relationship from collection

I have:
User->hasMany('Order')
...
Order->hasMany('Product')
...
$users=User::with('orders','orders.product')->...
how can I retrieve all products bought from users in $users taking advantage of eager loading instead of doing other queries?
You could use a hasManyThrough relationship on your User model.
https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
public function products()
{
return $this->hasManyThrough('App\Product', 'App\Order');
}
or, using a collection, as your already eager loading.
$products = User::with('orders','orders.product')->flatMap(function(User $user) {
return $user->orders->flatMap(function(Order $order) {
return $order->products;
});
});

Laravel pass variable from controller query to model function

I have a function on my model that check active status in equipment_station table,
originally it only checked one status, now i need to pass a parameter to the model with active or inactive values, I've tried this with no success.
controller:
model :
How can i change the parameter in the model, I send status from controller as inactive or active.
thanks in advance
You could you query scope
public function scopeActive($query, $active)
{
return $query->where('active', $active);
}
then you can call it in your model
Estacion::active(true)->get();
You could Constraint Eager Loading by specifying additional query constraints for the eager loading
$historial_estaction =
Estacion::where('estacion.id', $id)
->whereHas('equipos', function($query) use ($estado1) {
$query->where('equipo_estacion.estado', $estado1);
})->with(['equipos' => function ($query) use ($estado1) {
$query->where('estado', $estado1);
}])->get();
Model:
public function equipos()
{
return $this->belongsToMany('App\Equipo')
->withPivot('estado')
->withTimestamps();
}

Eloquent withtrashed() for soft deletes on eager loading query - Laravel 5.1

I'm trying to use withTrashed() with eager loading. I've created a new _withtrashed function in my model, but the query comes back with a NULL for my client
Client model:
// Client
public function client()
{
return $this->belongsTo('App\Client');
}
// Client (withtrashed for soft deletes)
public function client_withtrashed()
{
return $this->belongsTo('App\Client')->withTrashed();
}
Order controller:
/**
* Show order.
*/
public function show($id)
{
$order = Order::with('client_withtrashed') ---> it works normally with: Order::with('client') , except I don't get the soft deleted rows
->where('id', '=', $id)
->firstOrFail();
dd($order); displays an empty client
#relations: array:1 [
"client_withtrashed" => null
Any ideas? I decided on the solution above because I couldn't get withTrashed() to work with my eager query $order = Order::with('client_withtrashed')->withTrashed()
Well you can't define that on a relationship, but you can add constraints when you are eager loading:
$order = Order::with(['client' => function ($query) {
$query->withTrashed();
}])
->where('id', '=', $id)
->firstOrFail();
You can check the eager loading constraints in the docs
EDIT:
You can define withTrashed() on your relationship, just make sure that your models are using the SoftDeleteTrait.
use Illuminate\Database\Eloquent\SoftDeletingTrait;
class Client extends Eloquent {
use SoftDeletingTrait;
}
In my case
\App\User::withTrashed()->findOrFail($userId)->first()
is giving me the wrong data (was giving the auth user data) then,
\App\User::withTrashed()->whereId($userId)->first()
gave me the exact data.
Else,
I believe you can do:
\App\User::withTrashed()->whereId($userId)->firstOrFail();
instead of \App\User::withTrashed()->whereId($userId)->first() only to make more secured.

Resources