how show nested relation in laravel resource - laravel

I have Category Model that has one to many relation with itself.
It means each category has many children and each child has many products.
Now, I want to show parent categories (it means parent_id is null) with all products (list of products of all children).
Each product has category_id that category is child.
What is the best way to handle this in Laravel resources?
Category Model
class Category extends Model
{
public function products()
{
return $this->hasMany('App\Models\Products', 'category_id', 'id');
}
public function children()
{
return $this->hasMany('App\Models\Category', 'parent_id', 'id');
}
}
My Query:
$categories = Category::select(['id', 'name'])
->where('parent_id', '=', null)
->with(['children' => function ($query){
$query->select(['id']);
$query->with('products:id,title,description,banner');
}])
->orderBy('id', 'desc')
->get();
And Resource:
public function toArray($request)
{
return [
'id' => $this->id,
'category' => $this->name,
'products' => [],
];
}
I tried many different ways to show products, none of them has worked so far.

I used hasManyThrough relation to get all product of each main category
Relation function:
public function childrenproducts() {
return $this->hasManyThrough( Product::class, Category::class , 'parent_id', 'category_id' );
}
Query:
$categories = Category::select(['id', 'name'])
->where('parent_id', '=', null)
->has('childrenproducts', '>=', 1)
->with(['childrenproducts' => function ($query) {
$query->select(['products.id', 'products.title', 'products.description', 'products.banner']);
$query->orderBy('products.id', 'desc');
}])
->orderBy('id', 'desc')
->get();
Resource:
public function toArray($request)
{
return [
'id' => $this->id,
'category' => $this->getName(),
'products' => ProductResource::collection($this->whenLoaded('childrenproducts'))
];
}

Related

Count data slow use relationship in laravel?

$user = User::select(['id', 'name'])
->withCount([
'posts as success' => function ($query) {
$query->where('status', 0);
},
'posts as error' => function ($query) {
$query->whereIn('status', [1, 2, 3]);
},
])
->where('order', 3)
->get();
Model Post.php:
public function users() {
return $this->belongsTo(User::class);
}
Model User.php :
public function posts() {
return $this->hasMany(Post::class);
}
I want to count status in the post table through relationship. Like the above code, I got the result I wanted. But it is very slow, about 10 seconds or more. Is there any way to fix it? My post table has 400,000 data
I have index the status column in the post table.

Laravel - Leave count query not giving desired result

I am using Laravel-5.8 to get the count of employees that have applied for leave and those that have not applied for a particular year.
I have these 3 tables: hr_employees, hr_departments and hr_leave_requests.
class HrDepartment extends Model
{
protected $table = 'hr_departments';
protected $fillable = [
'id',
'company_id',
'dept_name',
];
}
class HrEmployee extends Model
{
protected $table = 'hr_employees';
protected $fillable = [
'id',
'company_id',
'first_name',
'last_name',
'department_id',
];
public function department()
{
return $this->belongsTo('App\Models\Hr\HrDepartment', 'department_id', 'id');
}
}
class HrLeaveRequest extends Model
{
protected $table = 'hr_leave_requests';
protected $fillable = [
'id',
'company_id',
'leave_status',
'employee_id',
];
public function department()
{
return $this->belongsTo('App\Models\Hr\HrDepartment', 'department_id', 'id');
}
}
An employee can apply for a leave several times in a year, but it will be counted as one. A department has many employees. Here is my code below:
$leaveReports = DB::table('hr_departments AS d')
->leftJoin('hr_employees AS e', function ($join) use ($userCompany) {
$join->on('d.id', '=', 'e.department_id')
->where('e.company_id', '=', $userCompany)
->where('e.hr_status', '=', '0');
})
->join('hr_leave_requests AS lr', function ($join) use ($userCompany) {
$join->on('e.id', '=', 'lr.employee_id')
->where('lr.company_id', '=', $userCompany)
->where('lr.leave_status', '!=', '0');
})
->where('d.company_id', '=', $userCompany)
->select(
'd.dept_name',
DB::raw('COUNT("lr.id") as applied_count'),
)
->groupby('lr.employee_id')
->get();
I want to display the result below:
I want to list all the departments, count the number of employees that have applied for leave and those that have not. if leave_status is not 0, then employee_id has applied for leave. To get applied in a department, subtract total applied in that department from total employee in that department.
If I have 3 departments and 50 employees. It shows all the departments and show the count those that have applied and those not not applied per department
However, instead of get the type of result in the diagram, it calculated all the employees as total applied.
How do I resolve this?
Thanks
Option 1: Eloquent Relationships.
You could do it by using the withCount method. But for that you need to first define the relationships in your HrDepartment model.
class HrDepartment extends Model
{
protected $table = 'hr_departments';
protected $fillable = [
'id',
'company_id',
'dept_name',
];
public function employees()
{
return $this->hasMany('App\Models\HrEmployee', 'department_id', 'id');
}
public function leave_requests()
{
return $this->hasManyThrough('App\Models\HrLeaveRequest', 'App\Models\HrEmployee', 'department_id', 'employee_id');
}
}
$departments = HrDepartment::select('hr_departments.dept_name')
->withCount([
'leave_requests as total_applied' => function ($query) {
$query->where('hr_leave_requests.leave_status', '=', 0);
},
'leave_requests as total_not_applied' => function ($query) {
$query->where('hr_leave_requests.leave_status', '!=', 0);
},
])
->where('hr_departments.company', '=', $userCompany)
->get();
Option 2: Query Builder
You can get the same result by copying and pasting the query eloquent makes in the base query builder but it doesn't really look pretty in comparison.
$departments = DB::table('hr_departments as d')
->select([
'd.dept_name',
'total_applied' => function ($query) {
$query->from('hr_leave_requests as lr')
->join('hr_employees as e', 'e.id', 'lr.employee_id')
->selectRaw('count(*)')
->whereColumn('d.id', 'e.department_id')
->where('lr.leave_status', '=', 0);
},
'total_not_applied' => function ($query) {
$query->from('hr_leave_requests as lr')
->join('hr_employees as e', 'e.id', 'lr.employee_id')
->selectRaw('count(*)')
->whereColumn('d.id', 'e.department_id')
->where('lr.leave_status', '!=', 0);
}
])
->where('d.company', '=', $userCompany)
->get();

creating a section for test where registered users can do it, but currently the test can be done many times and I wish they could only do it once

I'm using laravel 7
I need that if the user already took the test, load another different view
This is my test controller
public function index()
{
$categories = Category::with(['categoryQuestions' => function ($query) {
$query->orderBy('id')
->with(['questionOptions' => function ($query) {
$query->orderBy('id');
}]);
}])
->whereHas('categoryQuestions')
->get();
return view('client.test', compact('categories'));
}
public function store(StoreTestRequest $request)
{
$options = Option::find(array_values($request->input('questions')));
$result = auth()->user()->userResults()->create([
'total_points' => $options->sum('points')
]);
$questions = $options->mapWithKeys(function ($option) {
return [$option->question_id => [
'option_id' => $option->id,
'points' => $option->points
]
];
})->toArray();
$result->questions()->sync($questions);
return redirect()->route('client.results.show', $result->id);
}
}
This is the way the result table connects to the user table
This my migration to add relationship with a users table;
class AddRelationshipFieldsToResultsTable extends Migration
{
public function up()
{
Schema::table('results', function (Blueprint $table) {
$table->unsignedInteger('user_id');
$table->foreign('user_id', 'user_fk_773765')->references('id')->on('users');
});
}
}
Update your index method to check the results table. Assuming you have a Result model, for your results table.
public function index()
{
$existingTest = Result::where('user_id', Auth::user()->id)->first();
if(isset($existingTest->id)) {
//Return your other view or run logic specific to if the user has already done the test.
return view('client.test-complete');
}
$categories = Category::with(['categoryQuestions' => function ($query) {
$query->orderBy('id')
->with(['questionOptions' => function ($query) {
$query->orderBy('id');
}]);
}])->whereHas('categoryQuestions')
->get();
return view('client.test', compact('categories'));
}

subcategory not showing on admin panel

I'm editing a Laravel 4.3 site and I have a db table called categories which has the following fields:
id
parent_id
name
I'm trying to output a list in my view of categories, and their subcategories:
Category
Another Category
Subcat
Subcat
Subcat
I'm not really sure of the best way of achieving this and hoping someone can help point me in the right direction :-)
this is my controller
public function store(Request $request)
{
$this->validate($request, [
'name' =>'required'
]);
$category= new Category;
$category= Category::with('children')->whereNull('parent_id')->get();
$category->name =$request->name;
$category->save();}
I hope this would help you
Method to store the data:
public function store(Request $request) {
$this->validate($request, [
'name' =>'required',
'parent_id' => 'nullable|exists:category,id', //This will check the parent availability
]);
$category= new Category;
if ($request->has('parent_id')) {
$category->parent_id =$request->parent_id;
}
$category->name =$request->name;
$category->save();
return $category;
}
Method to retrieve all data:
public function index() {
$categories = Category::with('children')->all();
return $categories;
}
Method to get category by id :
public function categoryById(Category $category) {
$category = Category::with('children')->where('id', $category->id)->first();
return $category;
}

Get var from controller to model

I got one variable in controller ($id), I want to pass it to the model:
CONTROLLER:
public function user_load_more($id, $friendly_url)
{
$user = User::where('friendly_url', '=', $friendly_url)
->with('shares_load_more.links.tag', 'userProfile.get_following')
->with(['shares_load_more.links.page' => function ($query) {
$query->select('id', 'name', 'friendly_url');
}])->orderBy('id', 'desc')->first();
return view("site.list.user.links", compact("user"));
}
MODEL (User.php): (?)
public function shares_load_more($id) //--- put the id here?
{
return $this->hasMany(Share::class, 'user_id', 'id')
->select('id', 'link_id', 'user_id', 'shared_in', 'content', 'created_at')
->take(2)
->orderBy('id', 'desc')
->where('id', '<', $id)
->where('type', '=', 0);
}
You don't really need to pass variable in a model, there is an alternate method to do it.
public function user_load_more($id, $friendly_url)
{
$user = User::where('friendly_url', '=', $friendly_url)
->with('shares_load_more.links.tag', 'userProfile.get_following')
->with(['shares_load_more' => function($query) use($id) {
$query->where('id', '<', $id);
}, 'shares_load_more.links.page' => function ($query) {
$query->select('id', 'name', 'friendly_url');
}])->orderBy('id', 'desc')->first();
return view("site.list.user.links", compact("user"));
}
Model:
public function shares_load_more($id) //--- put the id here?
{
return $this->hasMany(Share::class, 'user_id', 'id')
->select('id', 'link_id', 'user_id', 'shared_in', 'content', 'created_at')
->take(2)
->orderBy('id', 'desc')
->where('type', '=', 0);
}
It will append to the relation query

Resources