yii hasone relation with condition - activerecord

I have a hasone relation in my code,
public function getDevice(){
return $this->hasOne(UserDevice::className(),['user_id' => 'id'])->where(['is_latest'=>true]);
}
In my query, I am trying to fetch user details.
$query = User::find();
$query->joinWith('device')->all();
If i am using getDevice without where condition, I am getting all users. But if I add where condition inside getDevice, I am getting users that have device details.
Is there anyway to list all users with the chceking.
That is I need to list all users, who has either details in device table or not

I have used this in my relation and it works.
public function getDevice(){
return $this->hasOne(UserDeviceInfo::className(),['user_id' => 'id'])->orOnCondition(['is_latest' =>1]);
}

Related

Laravel relationship between 3 tables where 2 are connected via pivot

I am new to Laravel and looked already for a similiar thread but I didnt find something.
I want to use Eloquent and I got 3 Models and Tables: Testseries, Devices and Users.
The Users has a many to many relation to devices. (One User has many devices and vica versa)
And Devices has a one to many relation to testseries. (One Device has many testseries and many testeries has one device)
**
Table structure Users:**
id
username
Table structure Devices:
id
serial_number <-- its a string, not important for the structure
Table structure Testseries:
id
device_id
Devices and Users are connected via Pivot
device_user:
id
user_id
device_id
If a User is logged in, I want to show all Testseries from all Devices that are connected to the User.
I defined in the User Model:
public function devices(): \Illuminate\Database\Eloquent\Relations\BelongsToMany {
return $this->belongsToMany(Device::class);
}
And in the Device Model:
public function users(): \Illuminate\Database\Eloquent\Relations\BelongsToMany {
return $this->belongsToMany(User::class);
}
public function testseries(): \Illuminate\Database\Eloquent\Relations\HasMany {
return $this->hasMany(Testserie::class);
}
Is there any way to create function inside the User Model which can easily access to the testserie?
If someone doesnt understand what I want because my English isnt good. This function should tell what I want inside the User Model:
public function testseries() {
return $this->devices()->testseries();
}
Also I want all testseries at one query.
I tried with the each method. But its doing for each device a single query to the testserie.
I also tried it with the with method. It works, but I want all Columns from the Testseries Table, but then I have to tell all table names inside the array and I dont want the columns from the Devices table.
I expected to get a query when I call the ->get Method that I'll get all Testseries at once with a single query.
Try this :
Auth::user()->with('devices.testseries')->get();
I found a way,
I added this function to my User Model:
public function testseries(): \Illuminate\Support\Collection {
return Testserie::whereHas('device', function(Builder $query){
$query->whereHas('users', function(Builder $query){
$query->where ('user_id', '=', \Auth::user()->getKey());
});
})->get();
}
This works. I dont know if its the cleanest way. If someone has a better way. Dont be shy to share your thoughts.

How to do the following with doesn't have or other optimized way in laravel

I have a thread which gave the answer but later on I found that I was getting limitations: how to get list of users who are not under belongsToMany relation under a table in laravel?
so creating a new thread where I do have an answer but now how can I optimize the same with any prebuild functions like doesntHave or something entirely else.
below is the code which gives me the list of users who are under a group and not assigned any task. one group can have multiple tasks so only users where the task is not assigned needs to be listed.
$gid = $task->group_id;
$MembersList = $task->members;
$group_subscribers = Group::with(['subscribedUsers' => function($q){
$q->select('id');
}])->whereId($gid)->get();
$group_subscribers = $group_subscribers[0]->subscribedUsers->pluck('id')->toArray();
$alreadyMembers = DB::table('task_user')->select('user_id as id')->whereIn('user_id', $group_subscribers)->pluck('id')->toArray();
$finalList = array_diff($group_subscribers, $alreadyMembers);
$users = User::whereIn('id', $finalList)->get();
return $users;
any way to improve the above code?
I guessed Users and Tasks was in a many to many relationship. For my optimisation to work, i added the following relationship to the User model, this is gonna be used when we filter the subscribed users.
public class Users {
public function tasks() {
return $this->belongsToMany(Task::class);
}
}
Basically the implementation is filtering all the subscriber users and checking if they have tasks, since tasks is a relationship it returns a Collection and isNotEmpty() is used for that reason.
$groupId = $task->group_id;
$group = Group::with(['subscribedUsers.tasks'])->find($groupId));
$withoutTasks = $group->subscribedUsers->filter(function ($user) {
return $user->tasks->isNotEmpty();
} );
return $withoutTasks;
It is a rule of thumb to avoid DB:: methods and you should be able to go to model to model, instead of making queries from getting from one model to another.

How do i access data using two BelongsTo?

I have three tables - "courses", "lessons" and "tasks". Each lesson belongsTo a course, and each task BelongsTo a lesson. I want to output a task, showing the task name, the lesson name, and the course name. How do I access the course table data? To get the lesson information linked to a course, I have used the following in my Task model:
$lessonName = $this->lessons->lesson_name;
To get the course name associated to that lesson, I have tried the following with no success, but I am really guessing here:
$courseName = $this->lessons->courses->course_name;
My model relationships are as follows:
Course.php
public function lessons()
{
return $this->hasMany('App\Lesson');
}
Lesson.php
public function tasks()
{
return $this->belongsTo('App\Task', 'task_id', 'id');
}
Task.php
public function lessons()
{
return $this->belongsTo('App\Lesson', 'lesson_id', 'id');
}
Where am I going wrong? Thanks
there is another way you can do this by using accessors.
on your Task model do the following:
public function getLessonAttribute(){
return Lesson::where('id', $this->attributes[*foreign_key_field*])->first();
}
Here you receive all the data regarding the lesson that the task belongs to, and can use them as any other attribute (field) of the model.
on your Lesson model get the course that it belongs to.
public function getCourseAttribute(){
return Course::where('id', $this->attributes[*course_foreign_key_field*])->first();
}
and then assuming that $task is your collection, you can access the lesson and the course like the following in blade:
$task->lesson->lesson_name and $task->lesson->course->course_name
In your lesson.php model doesn't exist relationship courses so there are your issue. Use answer what is told you #jeroenF
So you want the inverse of hasManyThrough?
The hasManyThrough feature of Laravel (see their site) facilitates connecting your Courses to Task directly, without having the intermediate connection in a separate relationship.
You are looking for the inverse?

Eloquent custom relation return type error

I'm having some troubles with a custom relation.
I want $user->sites to return every site $user got access to. It's a belongsToMany relationship, with a pivot table 'user_site'. The problem is, when $user is an admin, he has access to every site in the database, and I can't manage to get it to work with Eloquent.
public function sites() {
if( $this->is_super_user )
return Site::whereNotNull('deleted_at');
return $this->belongsToMany('Site', 'user_site');
}
This is what I got, but it isn't working (Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation).
I could just return Site::all() and $this->belongsToMany('Site', 'user_site)->get(), but I want to be able to use the dynamic property ($user->sites) and to filter the result ($user->sites()->where...)
Am I missing something ?

Laravel 4: How to add more data to Auth::user() without extra queries?

I'm rather new to Laravel 4 and can't seem to find the right answer, maybe you can help:
A User in our application can have many Accounts and all data is related to an Account, not a User. The account the User is currently logged into is defined by a subdomain, i.e. accountname.mydomain.com.
We added a method account() to our User model:
/**
* Get the account the user is currently logged in to
*/
public function account()
{
$server = explode('.', Request::server('HTTP_HOST'));
$subdomain = $server[0];
return Account::where('subdomain', $subdomain)->first();
}
The problem is that there is always an extra query when we now use something like this in our view or controller:
Auth::user()->account()->accountname
When we want to get "Products" related to the account, we could use:
$products = Product::where('account_id', Auth::user()->account()->id)->get();
And yet again an extra query...
Somehow we need to extend the Auth::user() object, so that the account data is always in there... or perhaps we could create a new Auth::account() object, and get the data there..
What's the best solution for this?
Thanks in advance
Just set it to a session variable. This way, you can check that session variable before you make the database call to see if you already have it available.
Or instead of using ->get(), you can use ->remember($minutes) where $minutes is the amount of time you wish to keep the results of the query cached.
You should take a look at Eloquent relationships : http://laravel.com/docs/eloquent#relationships
It provides simple ways to get the account of a user and his products. You said that a user can have many accounts but you used a first() in your function I used a hasOne here.
Using Eloquent relationships you can write in your User model:
<?php
public function account()
{
// I assume here 'username' is the local key for your User model
return $this->hasOne('Account', 'subdomain', 'username');
}
public function products()
{
// You should really have a user_id in your User Model
// so that you will not have to use information from the
// user's account
return $this->hasMany('Product', 'account_id', 'user_id');
}
You should define the belongsTo in your Account model and Product model.
With Eager Loading you will not run a lot of SQL queries : http://laravel.com/docs/eloquent#eager-loading
You will be able to use something like
$users = User::with('account', 'products')->get();
To get all users with their account and products.
I think this is a good example for the purpose of Repositories.
You shouldn't query the (involved) models directly but wrap them up into a ProductRepository (or Repositories in general) that handles all the queries.
For instance:
<?php
class ProductRepository
{
protected $accountId;
public function __construct($accountId)
{
$this->accountId = $accountId;
}
public function all()
{
return Product::where('account_id', $this->accountId)->get();
}
}
//now bind it to the app container to make it globaly available
App::bind('ProductRepository', function() {
return new ProductRepository(Auth::user()->account()->id);
});
// and whenever you need it:
$productRepository = App::make('ProductRepository');
$userProducts = $productRepository->all();
You could group the relevant routes and apply a filter on them in order to bind it on each request so the account-id would be queried only once per repository instance and not on every single query.
Scopes could also be interesting in this scenario:
// app/models/Product.php
public function scopeCurrentAccount($query)
{
return $query->where('account_id', Auth::user()->account()->id);
}
Now you could simply call
$products = Product::currentAccount()->get();

Resources