Counting a many to many relationship? - laravel

I have a many to many relationship between users and images.
User Model
public function image()
{
return $this->belongsToMany('\App\Image');
}
Image Model
public function user()
{
return $this->belongsToMany('\App\User');
}
Tables
users
id | name
images
id | url
image_user
id | image_id | user_id
When a user 'favourites' an image, it's stored in the pivot table.
id | image_id | user_id
1 1 1
2 2 1
3 1 2
I need a count of each images favourites.
I try something like:
Image::with('user')->find(1)->count();
But this counts the number of users, not the number of favourites.
Ideally I would like to return all of the image data along with a count of the user data - how can I do this?

You can do this:
$image = Image::with('user')->find(1) // Get the image with user eager loading
$image->name; // Access any attribute
$image->users->count(); // Get the user count
You can even add a few lines in your Image model to create a "custom" attribute:
public function getFavoritesAttribute()
{
return count($this->users);
}
And then use it like this:
$image->favourites;
There is a more detailed solution here:
Laravel many to many loading related models with count

Related

Get Departmenr name from department id using eloquent laravel

I have this user table
id | name | email | department_id
1 user 1 xyz#gmail.com 2
and this is department table
id | department_name
1 Admin
2 Account
there is salary table :
id | user_id | basic_pay
1 1 5000
I have this employee relation in salary model
class Salary extends Model
{
public function employee(){
return $this->belongsTo('App\User','user_id','id');
}
}
I want to get department name also to which user is associated
$Data = Salary::where('id',$id)->with('employee')->first();
but presently i can only get department_id with the help of employee relation.
Any help is highly appreciated.
You could add another relationship to the User model called department which would look like this:
public function department()
{
return $this->belongsTo(Department::class);
}
and then user dot notation documented here: https://laravel.com/docs/8.x/eloquent-relationships#nested-eager-loading. Which would look like this
Salary::where('id', $id)->with('employee.department')->first();
Additionally, you can simplify this by using the find method, which would look like this:
Salary::with('employee.department')->find($id);
You would need to reverse the other of methods due to the fact that find will return the model, in this case a Salary model.
If you do not know if the Salary will exist you may want to use findOrFail documented here https://laravel.com/docs/8.x/eloquent#not-found-exceptions
Data = Salary::where('id',$id)->with('employee.department')->first();
In Your Employee model add relation like Below
public function department(){
return $this->belongsTo('App\Department','department_id');
}

Searching and getting specific users in Laravel

I have relationship between users and i want to search and get collection from connected users. Example:
Users:
ID: 1 | Nikola - he is TRAINER
ID: 2 | Test - he is his client
ClientTrainer Table:
trainer_id | client_id | status
1 | 2 | 1
When trainer search for specific users, i want to search only users who has status 1 and have relationship between them.
This is my current code:
if(isset($_GET['type'])){
$names = explode(" ", $_GET['type']);
$klijenti = User::where(function($query) use ($names) {
$query->whereIn('name', $names);
$query->orWhere(function($query) use ($names) {
$query->whereIn('surname', $names);
});
})->orderBy("last_seen","DESC")->get();
print_r($klijenti);
if(empty($_GET['type'])){
$klijenti = $user->allclients;
}
}
I don't know if I fully understand your requirement, but here is one solution that utilizes relationships:
Add this to your User Model. This assumes that your "ClientTrainer" Table is called client_trainers.
public function clients()
{
return $this->belongsToMany('App\User', 'client_trainers', 'trainer_id', 'client_id')
->wherePivot('status', 1);
}
Option 1
Now you should be able to perform queries like this, assuming that Auth::user() returns the currently logged in trainer:
$clients = Auth::user()->clients()->whereIn('name', $names)
->orWhereIn('surname', $names)
->orderBy("last_seen","DESC")->get();
Or to get all the clients of a single trainer just do something like
$clients = Auth::user()->clients()->get();
Option 2
If you want to search independently of a single trainer, you can do something like this:
$clients = User::has('clients')
->whereIn('name', $names)
->orWhereIn('surname', $names)
->orderBy("last_seen","DESC")->get();
The first part User::has('clients') should only return Users that have at least on client with status = 1 in the client_trainers table.

Laravel / hasMany / 3 tables single callback

I have 3 tables like so:
User:
- user_id
- name
Location_user:
- loc_id
- loc_loc_id
- loc_user_id
Location:
- location_id
- location_name
So the users may belong to 4 locations so the "location_user" table will hold 4 records for that user.
At the moment I can call the users and get back a list of there locations but on an ID only basis using:
public function locations()
{
return $this->hasMany('App\Location_user', 'loc_user_id');
}
As I said this will come back with all the locations this user belongs too but I want to get the names of those location instead of the ID's or even both.
Any ideas? I am fairly new to laravel still so not had to do this yet.
EDIT:
Called by:
$results = User::with('locations')->get();
What you want is a many to many relationship not hasMany. Change your function to
public function locations()
{
return $this->BelongsToMany('App\Location_user', 'Location_user');
}
Ended up using:
public function locations() {
return $this->belongsToMany('App\Location', 'location_user', 'loc_user_id', 'loc_loc_id')->withTimestamps('loc_updated','loc_created');
}
Because I used custom column names I specified these and also included time-stamps as I needed those

Laravel 5 - setting up relationships

I have been struggling a little with my application and asked a lot of questions, and before I go any further, I just want to make sure that my relationships are ok.
So I am creating an application whereby you can generate different documents based on the input you provide. So, I create a project, and within that project there is a select field which has different types of documents. If I select DocumentA for example, the create form for DocumentA will be displayed to the user. If I choose DocumentB, the create form for DocumentB will be displayed. Now although these forms are different and take different inputs, I wanted a way whereby I would not have to create a new table for every document type. So I came up with the following.
So within a project, I select I want to create DocumentA. I am then displayed the view documentA.create. This view has hidden inputs for the document name and description. The form fields labels are the key in the document_data table, and the value is the input for this field. So, if I create DocumentA, my database might look like this
project
id | projectName |
------------------
1 | Project One |
------------------
document
id | projectId | documentName |
--------------------------------
1 | 1 | DocumentA |
--------------------------------
document_data
id | documentId | key | value |
----------------------------------------------
1 | 1 | clientName | Google |
----------------------------------------------
2 | 1 | projectName | Analytics |
----------------------------------------------
3 | 1 | Contact | Mr Sharp |
----------------------------------------------
4 | 1 | startDate | 29/12/2016 |
----------------------------------------------
Where I am struggling at the moment is that the foreign key documentId is in the document_data table. However, things are only working if I set a foreign key in both of my Models classes e.g.
class Document extends Model
{
protected $table = 'documents';
protected $guarded = [];
public function documentData()
{
return $this->hasMany('App\DocumentData', 'documentId');
}
}
class DocumentData extends Model
{
protected $table = 'document_data';
protected $guarded = [];
public function document()
{
return $this->belongsTo('App\Document', 'documentId');
}
}
If I dont set it in both classes, I get a MethodNotAllowedHTTP exception with no information about it. I have been able to create documents without problem, the problem comes when I need to update. The edit page for DocumentA has a form starting like this
{{ $document }}
{!! Form::model($project->document, [
'class'=>'form-horizontal',
'method' => 'PATCH',
'route' => ['projects.documents.update', $project, $project->document]
]) !!}
Now when I output $document above, I get the correct document that I am working on, as I should do. However, in the update function, if I output $document on its own or if I do
public function update(Request $request, Project $project, Document $document)
{
dd($project->document);
return null;
}
I see both DocumentA and DocumentB. Shouldnt the update only be passed DocumentA?
Why would this be? Any information or advice hugely appreciated.
Many thanks
I think the issue is the relationship between project and document. You probably have document belongsTo project, and project hasMany document, as you can have many documents with projectId = 1. Isn't it?
It what I said is okay, when you write $project->document, it brings you all the documents that belong to that project. It's confusing because you named the relationship 'document' instead of 'documents'.
You can do 2 things:
1- If each project can have only 1 document, change the relationship in the 'Project' model to hasOne Document. Then, if you do $project->document it will bring you only one.
2- If your app allows a project to have multiple documents, leave the relationship as hasMany (I'd recommend to rename it to documents instead of document), and pass the $document object to the update form, instead of passing the $project and trying to access the document from there.
Please let me know if I missed the point of your question and I'm totally wrong

Usage of Eloquent objects as dictionary tables in Laravel

I have different classes that act as "dictionary" lists, for example, for CarType class I hava table with id, value fields (1, 'small'; 2, 'big' ..)
Is there a way I can use such model as a map list (with cached data in memory) to have sometthing like that in my blade files:
{{ CartType->value($model->type_id) }}.
The idea would be
to load in memory all the data (or at leas after 2 requests of
different values)
to be easily to wrap around some fields of the model
You can do this with Eloquent (using relationships, as #TimLewis suggests), but it if's a simple map with just a few values (ie. not hundreds of different options), you'll probably find that Eloquent adds a lot of overhead for such a simple function.
Instead, I'd use database queries inside custom accessors. For example,
Imagine you have a Car Eloquent model and each car has exactly one "car type" value (denoted by a car_type field in the database).
Imagine that you also have a table in your database for listing car types, called (not surprisingly) car_types.
Here's a quick diagram:
|------------------|
|cars | |------------------|
|------------------| |car_types |
|id INT PRIMARY_KEY| |------------------|
|car_type_id INT | -- one-to-many --> |id INT PRIMARY_KEY|
|... | |name CHAR |
|------------------| |------------------|
You could set up an Eloquent model for car_type and define a relationship. That would work just fine, but do you really need all the functionality of an Eloquent model for this?
Instead, you could set up an accessor on the cars model:
class Car extends Eloquent {
public function getCarTypeAttribute()
{
return DB::table('car_types')
->select('name')
->where('id', '=', $this->car_type_id);
}
}
This will perform better than a full Eloquent model plus relationships.
But you also asked about caching the list. You could easily expand the example above to store the full list in the cache instead of querying each time:
class Car extends Eloquent {
public function getCarTypeAttribute()
{
$list = Cache::get('car_types_list');
if (!$list) {
$list = DB::table('car_types')->select('name');
// save in the cache for 60 minutes
Cache::put('car_types_list', $list, 60);
}
// search the list for the type ID
foreach ($list as $type) {
if ($type->id===$this->id) {
return $type;
}
}
// the type wasn't found
throw new Exception("That's not right!");
}
}
Now, if you have a Car model, you can get the type like this:
$car = Car::find(1234);
$type = $car->CarType;

Resources