I Have this 3 tables like below :
Tools
Parts
Part_details
it is my the table structure :
Tool -> has many -> parts. part -> has many->part_details.
Tool : id*, name; Parts : id*, name, tool_id; part_details: id, part_id, total;
Question :
Using laravel Model, how can I get Tool with One part that has biggest total on parts_details ??
// Tool Model
public function parts(){
return $this->hasMany(Part::class);
}
// Part Model
public function part(){
return $this->belongsTo(Tool::class);
}
public function part_details(){
return $this->hasMany(PartDetail::class);
}
// PartDetail Model
public function part(){
return $this->belongsTo(Part::class);
}
Now query the Tool model
$tools = Tool::with('parts')->withCount('parts.part_details')->get();
$toolWithMaxCount = $tools->filter(function($tool) use ($tools){
return $tool->parts->max('par_details_count') === $tools->max('parts.part_details_count');
})->first();
You can improve this with adding some raw bindings to optimise it. I think you got the idea.
Tool model
public function parts() {
return $this->hasMany('App\Part');
}
Part Model
public function details() {
return $this->hasMany('App\PartDetail');
}
public function tool() {
return $this->belongsToMany('App\Tool');
}
Detail Model
public function part() {
return $this->belongsToMany('App\Part');
}
Controller
$tools = Tool::with('parts', 'parts.details')
->find($id)
->max('parts.part_details');
Use the the hasManyThrough Relationship to get the all part details related to tool and then you can check the one by one record and get the highest total of the tool part.
// Tool Model
public function partsdetails()
{
return $this->hasManyThrough('App\PartDetail', 'App\Part','tool_id','part_id');
}
In Your controller
$data = Tool::all();
$array = [];
if(isset($data) && !empty($data)) {
foreach ($data as $key => $value) {
$array[$value->id] = Tool::find($value->id)->partsdetails()->sum('total');
}
}
if(is_array($array) && !empty($array)) {
$maxs = array_keys($array, max($array));
print_r($maxs);
}
else{
echo "No Data Available";
}
Related
class Parent extends Model
{
public function kids()
{
return $this->belongsToMany('App\Models\Kid')
->orderBy('age')
->withTimestamps();
}
public function oldestKid()
{
return $this->belongsToMany('App\Models\Kid')
->orderByDesc('age')
->take(1);
}
}
Problem with this approach is that $parent->oldestKid returns an array. It would feel more logical if it would return an object.
$parent = App\Models\Parent::with('oldestKid')->first();
This is what we ended up with, and its working.
Important to add: the pivot table is kid_parent
public function oldestKid()
{
return $this->belongsTo(Kid::class, 'oldest_kid_id', 'id');
}
public function scopeWithOldestKid($query)
{
$query->addSelect(['oldest_kid_id' => KidParent::select('kid_id')
->whereColumn('parent_id', 'parents.id')
->join('kids', 'kids.id', '=', 'kid_parent.kid_id')
->orderByDesc('kids.age')
->take(1)
])->with('oldestKid');
}
then you can use it like that :
$parents = Parent::withOldestKid()->get();
foreach($parents as $parent){
$oldest_kid = $parent->oldestKid;
}
If you want to go crazy: you can use https://laravel.com/docs/8.x/eloquent#global-scopes so it is always loaded in if you go for a parent.
You have to use a subquery to do that :
public function oldestKid()
{
return $this->belongsTo(Kid::class);
}
public function scopeWithOldestKid($query)
{
$query->addSelect(['oldest_kid_id' => Kid::select('id')
->whereColumn('parent_id', 'parents.id')
-> orderByDesc('age')
->take(1)
])->with('oldestKid');
}
then you can use it like that :
$parents = Parent::withOldestKid()->get();
foreach($parents as $parent){
$oldest_kid = $parent->oldestKid;
}
I'm trying to figure out how I can get a nested relationship but without any success.
Modal: Workout
public static function getWorkout($workout_id = null)
{
if ($workout_id) {
return Workout::whereId($workout_id)->with('exercises.sets')->first();
}
return [];
}
public function exercises()
{
return $this->belongsToMany('App\Models\Exercise');
)
Modal: Exercise
public function sets()
{
return $this->hasMany('App\Models\Set');
}
This solution gives me all sets based on "exercise_id". I need to get only the sets within the workout.
If I do this it works, the problem is now how I should get the ID of the workout to pass. I've tried to put the relation in the Workout Model as well but then the response of sets will get outside the exercise array.
public function sets()
{
return $this->hasMany('App\Models\Set')->where('workout_id', 5);
}
Try the following snippet where I specify a condition for the eager load.
public static function getWorkout($workout_id = null)
{
if ($workout_id) {
return Workout::whereId($workout_id)->with(['exercises.sets' => function($query) use($workout_id)
{
$query->where('workout_id', $workout_id);
}])->first();
}
return [];
}
When I want users not to be able to enter an individual resource I can use policies to do the following:
public function view(User $user, Model $object)
{
if($user->groupName != $object->groupName) {
return false;
} else {
return true;
}
}
This has as a result that the Components of your group have the eye icon (see red cirle). Components I do not want the user to see dont have the eye icon.
My desired result is that the should not be seen component is not shown at all. How can I achieve this?
I tried:
public function viewAny(User $user)
{
// $object does not exist here so I cannot use it to filter
if($user->groupName == $object->groupName) {
return true;
} else {
return false;
}
}
You need to update the index query of your resource. see more
public static function indexQuery(NovaRequest $request, $query)
{
return $query->where('groupName', $request->user()->group_name);
}
You should consider updating the relateble query too.
I'm trying to build an alternative relationship that returns all records instead of only related records. I have tried returning a query builder, but that doesn't work, it must be a relationship. What should I return to make this work?
public function devices()
{
if ($this->admin) {
// return all devices relationship instead
} else {
return $this->belongsToMany('Device', 'permissions');
}
}
Fiddle: https://implode.io/XXLGG8
Edit: I'd like to continue building the query in most cases, not just get the devices.
The devices() function in your model is expected to return a relation, you shouldn't add the if statement there. Make your devices() function like this:
public function devices()
{
return $this->belongsToMany('Device', 'permissions');
}
In your User model add a new function:
public function getDevices() {
if($this->admin === true) {
return Device::all();
}
return $this->devices();
}
Now you can do:
$admin->getDevices(); // will return all devices
$user->getDevices(); // will return only relations
I actually went a slightly different way and used a scope:
protected function scopeHasAccess($query, User $user)
{
if ($user->admin) {
return $query;
}
return $query->join('permissions', 'permissions.device_id', "devices.id")
->where('permissions.user_id', $user->user_id);
}
Add devices accessor method to the User model and implement your logic there.
public function getDevicesAttribute() {
if ($this->admin) {
return Device::all();
}
return $this->getRelationValue('devices');
}
See updated "fiddle".
I am trying to filter my Eloquent model collections based on if a user has access to the model or not.
My current method works, but it is really slow so I am wondering if there is a more performant way to do it?
I have a userHasAccess() method on every model in the collection.
It uses Laravel's ACL Features to determine if the user has access to the model:
public function userHasAccess()
{
if (Auth::user()->can('show', $this)) {
return true;
}
return false;
}
I then override the newCollection() method on the model:
public function newCollection(array $models = Array())
{
$collection = new Collection($models);
$collection = $collection->filter(function($model)
{
if($model->userHasAccess())
return true;
});
return $collection;
}
The policy method looks like this:
public function show(User $user, Quote $quote)
{
if(!$quote->customer)
return false;
if(($user->id === $quote->user_id))
return true;
if($user->hasRole(['super-admin','admin']))
return true;
return false;
}
Is there a better way to do this? Especially in terms of performance?
You could add the logic to the query and speed it up dramatically
$query = User::query();
if(!Auth::user()->hasRole(['super-admin','admin'])){
$query->where('user_id','=',Auth::id);
}
$data = $query->get();
You could do this on a wider scale using a scope
class User extends Model
{
public function scopeLimitByUser($query)
{
if(!Auth::user()->hasRole(['super-admin','admin'])){
$query->where('user_id','=',Auth::id);
}
}
}
Then for the quote customer you can add a where to the query
$query->whereNotNull('customer_id');