Laravel - Leave count query not giving desired result - laravel

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();

Related

How to combine these multiple queries in laravel eloquent

I have the following query, where the final result I want is the $rate
// get latest effective date
$effectiveDate = CpfEffectiveDate::where('effective_from', '<=', $currentDate)
->orderBy("effective_from", 'DESC')
->first();
// get scheme related to the effective date and citizenship type
$scheme = CpfScheme::where("cpf_citizenship_id", $request->cpf_citizenship_id)
->where('cpf_effective_date_id', $effectiveDate->id)
->first();
// get rate based on scheme and other data
$rate = CpfRate::where("cpf_scheme_id", $scheme->id)
->where("minimum_wage", '<', ceil($totalWage)) // query does not accept floats. should be acceptable as wage tiers should be integers
->where("minimum_age", '<', $request->employee_age)
->orderBy('minimum_wage', 'DESC')
->orderBy('minimum_age', 'DESC')
->first();
How can I combine all 3 queries into a single one?
First I get the correct effective date from the first table, after which I use it to find the correct scheme (together with a citizenship_id) which I use to find the correct rate.
Here are the following models:
CpfRate
class CpfRate extends Model
{
protected $table = "cpf_rates";
protected $primaryKey = "id";
protected $hidden = ["created_at", "updated_at"];
public function scheme()
{
return $this->belongsTo(CpfScheme::class, "cpf_scheme_id");
}
protected $fillable = [
"minimum_age",
"minimum_wage",
"employer_percentage",
"employee_percentage",
"employee_offset_amount", // used for special cases, such as -500 for percentage = 0.15 * (TW - 500)
"ordinary_wage_cap", // ordinary wage cap
];
}
CpfScheme
class CpfScheme extends Model
{
protected $table = "cpf_schemes";
protected $primaryKey = "id";
protected $hidden = ["created_at", "updated_at"];
public function citizenship()
{
return $this->belongsTo(CpfCitizenship::class, "cpf_citizenship_id");
}
public function effectiveDate()
{
return $this->belongsTo(CpfEffectiveDate::class, "cpf_effective_date_id");
}
}
CpfEffectiveDate
class CpfEffectiveDate extends Model
{
protected $table = "cpf_effective_dates";
protected $primaryKey = "id";
protected $hidden = ["created_at", "updated_at"];
// mutated to dates
protected $dates = ['effective_from'];
public function schemes() {
return $this->hasMany(CpfScheme::class, "cpf_effective_date_id");
}
}
CpfCitizenship
class CpfCitizenship extends Model
{
protected $table = "cpf_citizenships";
protected $primaryKey = "id";
protected $hidden = ["created_at", "updated_at"];
// fields
protected $fillable = ['description'];
public function schemes() {
return $this->hasMany(CpfScheme::class, "cpf_citizenship_id");
}
}
$rate = CpfRate::select('cpf_rates.*')
->where('cpf_scheme_id', '=', function ($query) use ($request) {
$query->select('id')
->from('cpf_schemes')
->where("cpf_citizenship_id", $request->cpf_citizenship_id)
->where('cpf_effective_date_id', '=', function($query, $currentDate) {
$query->select('id')
->from('cpf_effective_dates')
->where('effective_from', '<=', $currentDate)
->orderBy("effective_from", 'DESC')
->limit(1);
})
->first();
})
->where("minimum_wage", '<', ceil($totalWage)) // query does not accept floats. should be acceptable as wage tiers should be integers
->where("minimum_age", '<', $request->employee_age)
->orderBy('minimum_wage', 'DESC')
->orderBy('minimum_age', 'DESC')
->first();
I did not tested but what only here can be problem is $currentdate, so if it is problem just use Carbon class to get current date directly in query.

how show nested relation in laravel resource

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'))
];
}

Laravel Eloquent pass Attribute to Relationship

I'm trying to load a relationship with eager loading. But, would like to pass an attribute to the relationship function.
(Laravel 6.x)
Model - taxChildren() requires an ID
public function taxChildren($moduleId)
{
return $this->hasMany($this, 'parent')->where('module', '=', $moduleId')->orderBy('created_at', 'asc');
}
Controller
$departments = tax::where([
['module', '=', $this->departmentsModuleId],
])
->whereNull('parent')
->with([
'taxChildren' => ['moduleId', '2']
])
->get();
Now , i know that i can pass a query to my relationship like so:
$departments = tax::where([
['module', '=', $this->departmentsModuleId],
])
->whereNull('parent')
->with([
'taxChildren' => function($query){
$query->where('module', '=', $this->departmentsModuleId);
},
])
->get();
but i will be using this often with multiple relations that are similar. Anyone have an idea how to achieve this? Thanks!
You can't pass argument direcly to the relationship function but this can be done with model properties
Mymodel.php
class MyModel extends Model {
protected $moduleId = null;
public function taxChildren(){
return $this->hasMany($this, 'parent')->where('module', '=', $this->moduleId)->orderBy('created_at', 'asc');
}
}
MyController.php
use App\MyModel;
...
class MyController extends Controller {
public function controllerMethod(){
$query = new MyModel;
$query->moduleId = 1;
$departments = $query::where([
['module', '=', $this->departmentsModuleId],
])
->whereNull('parent')
->with(['taxChildren'])
->get();
}
}
Also you can use laravel query scopes to set modelId property . I think that is much cleaner code
Mymodel.php
class MyModel extends Model {
protected $moduleId = null;
public function scopeMudule($q, $module_id){
$this->moduleId = $module_id;
return $q;
}
public function taxChildren(){
return $this->hasMany($this, 'parent')->where('module', '=', $this->moduleId)->orderBy('created_at', 'asc');
}
}
MyController.php
use App\MyModel;
...
class MyController extends Controller {
public function controllerMethod(){
$departments = $query::module(1)->where([
['module', '=', $this->departmentsModuleId],
])
->whereNull('parent')
->with(['taxChildren'])
->get();
}
}

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

Laravel eloquent: Multiply two columns of two different tables

My Order Model
class Order extends Model
{
protected $fillable = [
'id','user_id', 'erp_id', 'currency_code','ex_rate_with_base','order_status','status','created_at'
];
protected $hidden = ['updated_at',];
public function orderList(){
return $this->hasMany(OrderList::class);
}
public function currency(){
return $this->belongsTo(Currency::class,'currency_code');
}
}
My Currency Model
class Currency extends Model
{
protected $fillable = [
'currency_code','currency_name', 'currency_symbol', 'ex_rate_with_base', 'update_via', 'status',
];
protected $hidden = [
'created_at','updated_at','updated_by','created_by',
];
protected $primaryKey = 'currency_code';
public $incrementing = false;
public function order()
{
return $this->hasMany(Order::class,'currency_code');
}
}
My OrderList Model
class OrderList extends Model
{
protected $fillable = [
'id','order_id', 'product_code', 'qty','unit_price','status',
];
protected $hidden = [
'created_at' ,'updated_at',
];
public function order(){
return $this->belongsTo(Order::class);
}
}
In my Order controller I want to run the query:
$order_history_list = Order::where([['user_id', $user->id], ['updated_at','>', $updated_at]])
->with([
'currency' => function ($query) {
$query->select('currency_code','currency_symbol','ex_rate_with_base');
},
'orderList' => function ($query) {
$query->select('id','order_id', 'product_code', '***order_lists.qty * orders.ex_rate_with_base AS unit_price_with_ex_rate***','status');
}
])->get();
But error is occuring due to the highlighted portion.
Error: Unknown column 'order_lists.qty * orders.ex_rate_with_base' in 'field list'
Please help me with the correct syntax
How can I use column of order table in the sub query ?
Use DB::raw in your select statement and add a join to the 'orderList' $query
$order_history_list = Order::where([['user_id', $user->id], ['updated_at','>', $updated_at]])
->with([
'currency' => function ($query) {
$query->select('currency_code','currency_symbol','ex_rate_with_base');
},
'orderList' => function ($query) {
$query->select('id','order_id', 'product_code', DB::raw('order_lists.qty * orders.ex_rate_with_base AS unit_price_with_ex_rate'),'status')
->join('orders','order_lists.order_id', 'orders.id');
}
])->get();
My Final Query That Worked:
$order_history_list = Order::where([['user_id',$user->id],['updated_at','>',$updated_at]])
->with(['currency'=>function($query){
$query->select('currency_code','currency_symbol','ex_rate_with_base');
},'orderList' => function($query){
$query->select('order_lists.id','order_lists.order_id', 'order_lists.product_code', 'order_lists.qty','order_lists.unit_price',DB::raw('he_order_lists.qty* he_orders.ex_rate_with_base AS unit_price_with_ex_rate'),'order_lists.status')->join('orders','order_lists.order_id', 'orders.id');
}])->get();

Resources