Laravel complicated relationship - laravel

My laravel app structed like this:
People: Model & Controller
Pets: Model & Controller. Has many to many relationship with People.
Abiilities: Model & Controller.
People_pets: People pets. (pivot table with people_id and pet_id). Also has 4 columns of abbility1_id, abbility2_id, abbility3_id and abbility4_id.
Now.. I built an API method to return the user pets, and it looks like this:
public function pets()
{
return $this->belongsToMany(Pet::Class, 'user_pets')->withPivot(
'abbility1_id',
'abbility2_id',
'abbility3_id',
'abbility4_id'
)->orderBy('position','asc');
}
which makes $user->pets returns the user list of pets, with the pivot information of user_pets and the abbility ids.
The question
What I want to do is add a json object to this method result named "abbilities" and get the data of the abbilities there such as name and description from the Abbilities model/controller.
How I can add the abbilities information to my query out of just the ID's?
The output of the current API CALL:
Desired output: array of Abbilities inside every pet object with the detail of the attack_id's inside $user->pets->pivot->attack1_id

You could just use the query builder and orWhere to grab the data. I don't think you can access it through relationships with the way you have it set up.
I don't know how you access your pet ability ids, but I'm guessing it's like $pet->abbility1_id.
$abbilities = Abbilities::where('id', '=', $pet->abbility1_id)
->orWhere('id', '=', $pet->abbility2_id)
->orWhere('id', '=', $pet->abbility3_id)
->orWhere('id', '=', $pet->abbility4_id)
->get();

Related

Contain Laravel 8 rightJoin data in its own property

I'm successfully doing a rightJoin in Laravel 8 Eloquent. I'd like to isolate data from the table I am joining as its own property on the object.
My Eloquent code:
return Book
::rightJoin('tableB', 'tableB.book_id', '=', 'book.id')
->where('tableA.user_id', $uid)
->get();
Right now the data I am getting is formatted as e.g.:
[
tableA.col1,
tableA.col2,
tableB.col1,
tableB.col2
]
I'd like to have the returned objected structured like this instead:
[
tableA.col1,
tableA.col2,
tableB [
col1,
col2
]
]
I'm doing this to get a list of books that the user has started, and want the progress info as its own property to satisfy the structure expected on the front end.
I could of course process and restructure the data using PHP on the back end, but this feels rather ugly. Is there a Laravel 8 Eloquent feature that can help me structure the returned data in the way the front end right now expects?
What you are looking for is a belongsTo (or hasOne depending on the case) relationship.
Models/Book.php
public function tableB() {
return $this->belongsTo(TableB::class);
}
Then when on the eloquent builder you can use with to retrieve the books and the TableB records associated with each book
$books = Book::whereHas('tableB', function($query) {
$query->where('id', ...); // this is a query that will put a constraint of which results are returned. Adjust according to your necessity
})->get();

Laravel: How to retrieve this nested model

In Laravel I have ModelA, ModelB and ModelC. ModelA has many ModelB. ModelB has many ModelC. I want to retrieve all ModelC for a selection of ModelA. How do I do this?
I tried the following:
$models = ModelC::with(['modelB','modelB.modelA' => function ($query) {
$query->where('owner', 123);
}])->get();
But the first query in that case is select * from model_c. Obviously not the result I am looking for.
Imagine that you were received 100 objects from the database, and each record had 1 associated model (i.e. belongsTo). Using an ORM would produce 101 queries by default; one query for the original 100 records, and additional query for each record if you accessed the related data on the model object. In pseudo code, let’s say you wanted to list all published authors that have contributed a post. From a collection of posts (each post having one author) you could get a list of author names like so:
$posts = Post::published()->get(); // one query
$authors = array_map(function($post) {
// Produces a query on the author model
return $post->author->name;
}, $posts);
We are not telling the model that we need all the authors, so an individual query happens each time we get the author’s name from the individual Post model instances.
Eager Loading
As I mentioned, ORMs “lazy” load associations. If you intend to use the associated model data you can trim that 101 query total to 2 queries using eager loading. You just need to tell the model what you need it to load eagerly.
Here’s an example from the Rails Active Record guide on using eager loading. As you can see, the concept is quite similar to Laravel’s eager loading concept.
$posts = Post::with('author')->limit(100)->get();
I find that I receive better understanding by exploring ideas from a wider perspective. The Active Record documentation covers some examples that can further help the idea resonate.
I managed to solve this with nested whereHas calls as follows:
$models = modelC::whereHas('modelB', function ($query) {
$query->whereHas('modelA', function ($query) {
$query->where('owner', 123);
});
})->get();
Laravel to the rescue, yet again!

view database data that was created today by a specific user

I'm creating a foodlog and I want a user to be able to search through the days and view food that was only logged that day.
For example, on the page for today's date I only want to see all the food items that I logged for today.
I have my table getting all data from that specific user from all dates, but how do I narrow it down to just today?
FoodlogController.php
public function show() {
$userId = Auth::user()->id;
$posts = Post::where('user_id', $userId)->get();
return view('foodlog', compact('posts'));
}
This is what my table looks like
I'm going to assume you have the default timestamps on, which add created_at and updated_at fields. If that's not the case, and you log time of creation with a different column, just replace the created_at with it.
Laravel comes with a hand package for dealing with dates - Carbon, which can be used by importing it from Carbon\Carbon namespace.
And Laravel's query builder has a method whereDate (scroll to "whereDate"), which can be used exactly for this.
Combining these 2, you could do this:
$posts = Post::where('user_id', $userId)->whereDate('created_at', \Carbon\Carbon::today())->get();
This will get the posts that were created today.
However
I would strongly recommend you create a hasMany relationship (user hasMany posts and inversely a post belongsTo a user), which would enable to use relationships and do something like this:
$posts = $user->posts()->whereDate('created_at', \Carbon\Carbon::today())->get();
And many more things, the Laravel way.

Load just ids of relation (pluck)

I have a simple app, using Laravel 5.5.13.
public function index()
{
return Pet::all();
}
This lists all pets. I have many to many relation where many users can own a the same pet (the pet's human family).
I want to load those users.
Doing return Pet::with('users')->get(); does the trick, however it loads all kind of excessive infromation, like the users api_token etc, I just want to pick some fields, like id and name:
I was hoping to just get users: [1, 12] for the example in the screenshot above.
I tried pluck like this return Pet::with('users')->get()->pluck('id') but this gives me only the ids.
You can select specific fields like this:
Pet::with(['users' => function($query) { $query->select('id', 'name'); }])->get()
If you're only looking to get user IDs where all the matching users have at least one pet, you can try:
// Retrieve all users that have at least one pet
return User::has('pets')->get(['id']);
In case I'm misunderstanding you and you still want all of the Pet information, you can use a colon to fetch specific columns in a relation:
// Returns all Pets, along with their users' IDs
return Pet::with('users:id')->get();
Querying Relations, Querying Relationship Existence
Eager Loading, Eager Loading Specific Columns

eloquent filter result based on foreign table attribute

I'm using laravel and eloquent.
Actually I have problems filtering results from a table based on conditions on another table's attributes.
I have 3 tables:
venue
city
here are the relationships:
a city has many locations and a location belongs to a city.
a location belongs to a venue and a venue has one location.
I have a city_id attribute on locations table, which you may figured out from relationships.
The question is simple:
how can I get those venues which belong to a specific city?
the eloquent query I expect looks like this:
$venues=Venue::with('location')->where('location.city_id',$city->getKey());
Of course that's not gonna work, but seems like this is common task and there would be an eloquent command for it.
Thanks!
A couple of options:
$venues = Venue::whereIn('location_id', Location::whereCityId($city->id)->get->lists('id'))
->get();
Or possibly using whereHas:
$venues = Venue::whereHas('location', function($query) use ($city) {
$query->whereCityId($city->id);
})->get();
It is important to remember that each eloquent query returns a collection, and hence you can use "collection methods" on the result. So as said in other answers, you need a Eager Loading which you ask for the attribute you want to sort on from another table based on your relationship and then on the result, which is a collection, you either use "sortBy" or "sortByDesc" methods.
You can look at an example below:
class Post extends Model {
// imagine timpestamp table: id, publish, delete,
// timestampable_id, timestampble_type
public function timestamp()
{
return $this->morphOne(Timestamp::class, 'timestampable');
}
}
and then in the view side of the stuff:
$posts = App\Post::with('timestamp')->get(); // we make Eager Loading
$posts = $posts->sortByDesc('timestamp.publish');
return view('blog.index', compact('posts'));

Resources