For an online competition with the goal of photographing certain objects I've got users uploading pictures of objects, where the objects have a category and a subcategory.
A user's dashboard should look something like this:
------------------------------------------------
| Category |
------------------------------------------------
| ----------------------------------------- |
| | Subcat. | Subcat. | |
| |------------------|--------------------| |
| |Object1 | Object2 | Object1 | Object2 | |
| |------------------|--------------------| |
| | Pic1 | Pic1 | Pic1 | Pic1 | |
| | Pic2 | Pic2 | Pic2 | Pic2 | |
| ---------------------------------------- |
| |
------------------------------------------------
In order to be able to output the pictures in the appropriate loop, I installed hasMany relationships between the models. So I can get an object with a convenient hierarchy by
$userPics = Category::with(['subcats','subcats.objects','subcats.objects.pics'])->get();
QUESTION:
For the dashboard of course I only want the images from a certain user: the Pictures model contains a user_id field. I could get the user's pictures directly: Pictures::where('user_id',$user->id)->get();, but then I don't have the structure in the object to create the desired view shown above.
How can I do something like this:
$userPics = Category::with([...])->where('user_id',$user->id)->get();
Many thanks for any tips!
You want to get categories with pictures that belong to specified user, so you do this (a bit verbose):
Category::with(['subcats', 'subcats.objects' => function ($q) use ($user) {
$q->with(['pictures' => function ($q) use ($user) {
$q->where('user_id', $user->id);
}]);
}])
->whereHas('subcats', function ($q) use ($user) {
$q->whereHas('objects', function ($q) use ($user) {
$q->whereHas('pictures', function ($q) use ($user) {
$q->where('user_id', $user->id);
});
});
})
->get();
You need to use nested with() closures here, because you can load more than two levels of relationship with using dot notation.
If you don't need to load pictures and subcategories data, just remove with() part.
If you also want to filter subcategories, add a closure to with() for subcats.
Related
I am currently trying to make a relation between 3 tables.
post
id
name
category
id
name
post_category
id
post_id
category_id
Database
post
| 1 | post1 |
| 2 | post2 |
| 3 | post3 |
category
| 1 | cat1 |
| 2 | cat2 |
| 3 | cat3 |
post_category
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 2 |
| 3 | 2 | 2 |
| 3 | 1 | 3 |
Model Post.php
public function getCategory()
{
return $this->belongsToMany(Category::class, 'post_category');
}
PostController.php
$data = Post::with('getCategory')->get();
It returns correct post list.
Now i want to filter the post by category. I try, but it not working
$categoryId = [1,2];
$data = Post::with('getCategory')->whereHas('category', function ($query) use ($categoryId) {
$query->whereIn('id', $categoryId);
})->orderBy('id','DESC')->get();
please help me
use Laravel 5.4
apparently everything is fine!
One suggestion is to add two more parameters to the belongsToMany method, like:
public function getCategory()
{
return $this->belongsToMany(Category::class, 'post_category', 'post_id', 'category_id');
}
https://laravel.com/api/7.x/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_belongsToMany
You should rename your getCategory() function to simply category(). That makes the relation names much more straightforward and probably fixes your issue.
THEN, you should be able to call whereHas('category', ...).
If it still does not work, simply chain a ->toSql() to any of your queries and debug the actual query that way.
I have three tables like these:
// users
+----+-----------+
| id | user_name |
+----+-----------+
| 1 | Jack |
| 2 | Peter |
| 3 | John |
+----+-----------+
// skills
+----+------------+
| id | skill_name |
+----+------------+
| 1 | PHP |
| 2 | HTML |
| 3 | Laravel |
| 4 | MySQL |
| 5 | jQuery |
+----+------------+
// skill_user
+----------+---------+
| skill_id | user_id |
+----------+---------+
| 2 | 1 |
| 5 | 1 |
| 1 | 2 |
| 2 | 2 |
| 4 | 2 |
+----------+---------+
Now I want to get all skills of one specific user. I can do that by using a join clause in a raw sql query:
SELECT s.skill_name
FROM skills s
JOIN skill_user su ON s.id = su.skill_id
WHERE su.user_id = :user_id
/* output (assuming :user_id is 1)
+------------+
| HTML |
| jQuery |
+------------+
Ok All fine. Now I want to know, how can I do that by adding a method in the model of Laravel framework? All I'm trying to do is using belongsTo(), hasOne(), hasMany() etc ..
To do in Eloquent way, you need to add the relationship. In your case, it is many-to-many relationship so in User.php, add this function to declare the relationship:
public function skills() {
$this->belongsToMany(\App\Skill::class, 'skill_user', 'user_id', 'skill_id');
}
Then,
Access it like this to eager load it:
$users = \App\User::with('skills')->get();
OR
Say you have a single user,
$user = User::where('id', 1)->first(); // Get the user with id = 1
and to get the skill for that user:
$user->skills;
OR
If you don't want to do using Eloquent way, you can use Query Builder. Note that, using this method, you basically join it manually so you don;t need to add any method to the Model:
\DB::table('users')
->join('skill_user', 'skill_user.user_id','=', 'users.id')
->join('skills', 'skill_user.skill_id', '=', 'skills.id')
->select('*')
->where('users.id', 1)
->get();
You have to use belongsToMany relationships. Assuming you will create SkillUser model for skill_user table. Add the following code to User Model
public function skills() {
$this->belongsToMany('App\SkillUser', 'skill_user', 'user_id', 'skill_id');
}
Now you can access it by
$users->skills();
I assume you are talking about Many to Many relationship between Users and skills, then the example given in Laravel many-to-many speaks about it.
I trying to create a search function in php with Slim and Eloquent 5.1, but i have a problem to get the results from the database.
This are my tables
Listing Table
id | title | description | price | size |etc...
-----|--------|-------------|-------|--------|-------
1 | list1 | some text | 500 | 50 |
2 | list2 | some text | 700 | 80 |
3 | list3 | some text | 350 | 120 |
Listings Option Table
id | id_listing | opt_name | opt_value
-----|-------------|-------------|----------
1 | 1 | rooms | 3
2 | 1 | baths | 4
3 | 2 | rooms | 8
4 | 3 | baths | 1
5 | 3 | rooms | 6
Listings Images Table
id | id_listing | file_name
-----|-------------|-------------
1 | 1 | file_1.png
2 | 1 | file_2.png
1 | 2 | file_3.png
2 | 3 | file_4.png
Now i found a way to get all the results with all they options with
listado = $app->listing->with(['options'],['imgs'])->get();
My classes
//Listing Class
class Listing extends Eloquent{
protected $table = 'listings';
public function options(){
return $this->hasMany('Casas\Listing\ListingOption', 'id_listing', 'id');
}
public function imgs(){
return $this->hasMany('Casas\Listing\ListingImg', 'id_listing');
}
}
// ListingOption class
class ListingOption extends Eloquent{
protected $table = 'listings_options';
public function listings()
{
return $this->belongsTo('Casas\Listing\Listing', 'id_listing');
}
}
// ListingImg Class
class ListingImg extends Eloquent{
protected $table = 'listings_imgs';
public function listings()
{
return $this->belongsTo('Casas\Listing\Listing', 'id_listing');
}
}
but i cannot find a way to filter the query the way i want to. I tried many thing.
Example: I want to get all the listings that have price less than 600 and have more than 2 rooms.
So that be listing id 1 and 3
If anyone can help create this query i'll appreciate that.
What you want want to use is whereHas.
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship
$result = $app->listing->with('options', 'imgs')
->whereHas('options', function ($query) {
$query->where('opt_value', '>', 2)
->where('opt_name', 'room');
})
->where('price', '<', 600)->get();
So what we are doing here is, eager loading options and imgs tables and fetching only the listings that have at least one option where opt_value is bigger than two and opt_name is room.
You can find more in info about querying relationships in here
I would try something like this:
$result = $app->listing->with(
['options' => function ($query) {
$query->where('opt_value', '>', 2);
}
],
['imgs'])->where('price', '<', 600)->get();
The last, outer where should be assigned to listing. The inner where, passed in a callback, should be assigned to options directly.
Take a look at the docs (the "Constraining Eager Loads" section).
Given the following table
gallery
+----+---------------+--------------+---------+
| id | gallery_title | viewcount | user_id |
+----+---------------+--------------+---------+
| 1 | Animals | 10 | 1 |
| 2 | Cars | 5 | 1 |
| 3 | Houses | 2 | 2 |
+----+---------------+--------------+---------+
user
+----+----------+
| id | username |
+----+----------+
| 1 | Bob |
| 2 | James |
+----+----------+
and the following classes
class Gallery extends Model
....
public function user()
{
return $this->belongsTo('App\User');
}
and
class User extends Model
....
public function galleries()
{
return $this->hasMany('App\Gallery');
}
calling $galleryCollections= Auth::user()->galleries; returns an array of collections in which I can iterate through
foreach ($galleryCollections as $galleryCollection)
{
$viewcount += $galleryCollection->viewcount;
}
print $viewcount; #returns 15
and so far everything works as expected, correctly up until this point.
However if I accidentally called $galleryCollection->sum('viewcount'), which is the last value from the iteration, the returned value is 17, as it's simply running the following SQL select sum('viewcount') as aggregate from 'gallery'.
I'm struggling to understand what exactly what is happening here. It's almost as if it's calling the 'sum()' method on the gallery class itself without passing in any 'where' values. I'd at least expect it to call the sum() method on the Collection, but instead it's going back to the database.
Is it because my Gallery class does not implement a 'sum()' method, and therefore it uses the Parent Model class and ignores the Gallery class?
If you want to count through sql, then:
Auth::user()->galleries()->sum('viewCount');
But, Auth::user()->galleries->sum('viewCount'); will sum on the collection Auth::user()->galleries.
Auth::user()->galleries() is a queryBuilder whereas Auth::user()->galleries is a collection of galleries of that user.
I'm using Laravel 5 and Eloquent, I have 3 tables setup:
photos
+----+---------------+------------------+
| id | photo_name | photo_location |
+----+---------------+------------------+
| 1 | kittens.jpg | C:\kittens.jpg |
| 2 | puppies.jpg | C:\puppies.jpg |
| 3 | airplanes.jpg | C:\airplanes.jpg |
| 4 | trains.jpg | C:\trains.jpg |
+----+---------------+------------------+
photo_set (pivot table)
+------------+----------+
| set_id | photo_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
+------------+----------+
sets
+----+----------------+
| id | description |
+----+----------------+
| 1 | cute animals |
| 2 | transportation |
+----+----------------+
I created a belongsToMany relationship in my photos and sets models to link these two together.
class Photo extends Model {
public function sets()
{
return $this->belongsToMany('App\Set');
}
}
and
class Set extends Model {
public function photos()
{
return $this->belongsToMany('App\Photo');
}
}
However I'm trying to reference the $Sets->photos() model function in my controller, so that given a set of id=1, I can return the photo_location value for each row [C:\kittens.jpg,C:\puppies.jpg], but I don't know how to access it..
Also, I can "sort of" access this information in the view with:
#foreach($sets as $set)
{{$set->images}}
#endforeach
but it looks like it only iterates through once and returns a json (?) of the necessary information, though I'm not sure how to parse that to regular HTML either.
So in short, I'm having trouble accessing this data (photo_location) from both the controller and the view
Use Collection::lists()
$locations = Set::find(1)->photos->lists('photo_location');
It will return an array ['C:\kittens.jpg', 'C:\puppies.jpg']
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Collection.html#method_lists
In your controller try this :
$result = PhotoSet::where('set_id', $set_id)
->with('sets')
->with('photos')
->get();
$photo_location = $result->photos->photo_location;
here PhotoSet is the model for photo_set table, because it is a pivot table we will be able to access both sets and photos table from it.