Not Sure if Laravel Distinct() is What I Should Use - laravel

This might be simple but I cant seem to figure it out. Customers have many machines. I need to return just the customer name once for a list. What I'm getting is an array of all machines.
public function machine_past_due(){
$today = date('Y-m-d');
$machines_past =Machine::distinct()
->whereDate('cert_date', '<', $today)
->with('customer')
->get();
return response()->json($machines_past);
}

I may be misunderstanding, and I know you already have an accepted solution, but if you want customers who have machines, it seems to me answers here are coming at it from the wrong direction - you don't want machines with customers, you want customers.
As a first step:
Customers::has('machines')->get();
will give you a list of customers who have at least 1 machine.
To incorporate your date restriction, you can use whereHas() to do something like:
Customers::whereHas('machines', function (Builder $query) {
$query->whereDate('cert_date', '<', date('Y-m-d'));
})->get();
This will give you all customers who have at least one machine matching the date condition. Note it does not include the machines - just a customer list.
If you want to include a customer's machines in the results, you have to then use with(), while constraining it with the same date condition:
Customers::whereHas('machines', function (Builder $query) {
$query->whereDate('cert_date', '<', date('Y-m-d'));
})->with(['machines' => function($query) {
$query->whereDate('cert_date', '<', date('Y-m-d'));
}])->get();
This will give you a list of all customers who have at least one machine matching the date condition, and list each customers' machines as a property of the customer, accessible as $customer->machines. You can then iterate over customers and do things like (in a blade view say):
#foreach ($customers as $customer)
{{ $customer->name }}, {{ $customer->machines->count() }} machines:
#foreach ($customer->machines as $machine)
{{ $machine->cert_date }}
// ... some other $machine stuff :-)
#endforeach
#endforeach

Here is the solution to achieve your goal:
public function machine_past_due(){
$today = date('Y-m-d');
$machines_past =Machine::distinct()
->whereDate('cert_date', '<', $today)
->with('customer')
->get();
$machines_past = $machines_past->pluck('customer.property_name')->unique(); //added this line
return response()->json($machines_past);
}

force the query to only return distinct results.
This is what distinct do, here is the source code,
so when you use distinct, it will check all the columns you selected which is duplicated.
You can use groupBy instead:
$machines_past =Machine::groupBy('customer_id')
->whereDate('cert_date', '<', $today)
->with('customer')
->get();
And if you are using mysql 5.7+ and you are using strict mode in laravel, you need to disable ONLY_FULL_GROUP_BY in laravel.

If what you are trying to return is an array of distinct customer names from your machine collection then laravel collection have a couple of methods that can help you. Assuming your customer is a "toOne" relationship and has a name attribute then:
public function machine_past_due(){
$today = date('Y-m-d');
$machines_past = Machine::whereDate('cert_date', '<', $today)
->with('customer')
->get();
$customer_names = $machines_past
->pluck('customer.name')
->unique()
->all();
return response()->json($customer_names);
}
The code will pluck the customer name from each of the machine returned from your query and removes all duplicate before finally converting it to an array.

Related

use of orWhere() in Eloquent

I have a eloquent query that I am running twice, but feel can be run once.
I'd want to return the values of the first where statement if exists otherwise check the second where statement which is the default in the query.
This is what I am currently doing:
$details = Telco::select('telcos.id AS telco_id', 'telcos.name AS telco_name')
->leftJoin('telco_prefixs', 'telco_prefixs.telco_id', '=', 'telcos.id')
->where('telco_prefixs.prefix', '=', $phone_number) // check if ndc exists
->first();
if ($details){
return $details;
}
return Telco::select('telcos.id AS telco_id', 'telcos.name AS telco_name')
->leftJoin('telco_prefixs', 'telco_prefixs.telco_id', '=', 'telcos.id')
->where('telcos.name', '=', 'Default') //default channel
->first();
I have a feeling this can be combined to something like below:
However, this fails as keeps executing the OrWhere clause.
Telco::select('telcos.id AS telco_id', 'telcos.name AS telco_name')
->leftJoin('telco_prefixs', 'telco_prefixs.telco_id', '=', 'telcos.id')
->where('telco_prefixs.prefix', '=', $phone_number) // if ndc exists
->Orwhere('telcos.name', '=', 'Default') //default channel
->first();
Someone help. Thanks
Try to add your where and or where condition as below.
Telco::select('telcos.id AS telco_id', 'telcos.name AS telco_name')
->leftJoin('telco_prefixs', 'telco_prefixs.telco_id', '=', 'telcos.id')
->where(function ($query) use($phone_number) {
$query->where('telco_prefixs.prefix', '=', $phone_number);
$query->Orwhere('telcos.name', '=', 'Default');
})->first();
I you want to use just eloquent a possible solution would be to have something like:
I suppose you have model called Telco and another one calle TelcoPrefix.
//Telco.php
//first we create a has many relationship with your telco_prefixs table.
public function telcoPrefixs(){
return $this->hasMany(TelcoPrefix::class);
}
Once you have that relationship you can use something similar to this code:
Telco::whereHas('telcoPrefixs',function($query, $phone_number){
return $query->where('prefix,'=',$phone_number);
})->select('id','name')->first();
This will compare the relationship and if it exists or has it will return your first record in one query.
Hope it helps at least to give a guide of what you can do.

How can I do the job of three foreach loop by one foreach loop?

actually I am new to Laravel. I am doing a project for making a meal management system.
my code in my controller is:
$days=DB::table('days')
->get();
foreach ($days as $day) {
$breakfast[]= DB::table('breakfast_menus')
->leftJoin('breakfast_items', 'breakfast_menus.item_id', '=', 'breakfast_items.id')
->where('breakfast_menus.day_id',$day->id)
->select('breakfast_items.item')
->get();
$lunch[]= DB::table('lunch_menus')
->leftJoin('lunch_items', 'lunch_menus.item_id', '=', 'lunch_items.id')
->where('lunch_menus.day_id',$day->id)
->select('lunch_items.item')
->get();
$dinner[]= DB::table('dinner_menus')
->leftJoin('dinner_items', 'dinner_menus.item_id', '=', 'dinner_items.id')
->where('dinner_menus.day_id',$day->id)
->select('dinner_items.item')
->get();
}
return view('admin.show_menu',compact('days','breakfast','lunch','dinner'));
I want to show the data in a table. Where there will be
<td>day</td>
<td>Breakfast </td>
<td>Lunch</td>
<td>Dinner</td>
You should really use Laravel relationships and do justice with the framework.
Create models for each table.
Define relationship like days has breakfast_manu. See this
Then you will be able to do it like this:
$days = Days::with(['breakfast_menus','lunch_items','dinner_items'])->get();
I can say the way you wrote are very very bad for performance, why don't you use another way round in sql?
$days = DB::table('days')
->leftJoin('breakfast_menus', function($q) {
$q->leftJoin('breakfast_items', 'breakfast_menus.item_id', '=', 'breakfast_items.id')
->where('breakfast_menus.day_id', $day->id);
})
->leftJoin('lunch_menus', function($q) {
$q->leftJoin('lunch_items', 'lunch_menus.item_id', '=', 'lunch_items.id')
->where('lunch_menus.day_id', $day->id);
})
->leftJoin('dinner_menus', function($q) {
$q->leftJoin('dinner_items', 'dinner_menus.item_id', '=', 'dinner_items.id')
->where('dinner_menus.day_id', $day->id);
})
->select('days.id', 'breakfast_items.item as breakfast', 'lunch_items.item as lunch', 'dinner_items.item as dinner')
->get();
I didn't try out the SQL, but you can get the idea.
Then you can foreach $days to show your data

Laravel Foreach sum result with while filter

Here my table
:chart_of_account_category
id
name
:chart_of_accounts
id
chart_of_accounts_category_id
:ledger_book
id
transaction_id
chart_of_accounts_id
:transaction
id
name
date
total
this is what i want to achieve
Controller
$chart_of_accounts_category = ChartOfAccountsCategory::get();
return view ('backend.ledger_book.index', compact('chart_of_accounts_category'));
Blade/View
#foreach ($chart_of_accounts_category as $account_category )
#foreach ($account_category->ChartOfAccounts as $item)
$item->LedgerBook->where('chart_of_accounts_id', $item->id)->sum('transaction.total'), 2)
#endforeach
#endforeach
i can get the sum thru this, but i still need to filter few things more, which i done it in blade. but cant get the filter works properly especially where like with symbol %, i would minimize query inside blade, is there anywhere i can get the result thru controller?
$item->LedgerBook->where('chart_of_accounts_id', $item->id)->where('transaction.date', 'like', '2019-06-%')->sum('transaction.total'), 2)
Instead of 'like' operator, you can use
->whereYear('transaction.date', '2019')->whereMonth('transaction.date', '06').
Also, I would suggest you to eager load ChartOfAccounts, LedgerBook and Transaction in your Controller. This will make your foreach loops much faster.
$chart_of_accounts_category = ChartOfAccountsCategory::with(['chartOfAccounts' => function ($query) {
$query->with(['ledgerBooks' => function ($query) {
$query->with(‘transaction’)
->whereHas(‘transaction', function ($query) {
$query->whereYear('date', '2019')
->whereMonth('date', '06');
});
}]);
}])
->get();

Finding a user with highest post created in last 24 hours, laravel Eloquent

How to find the user with the highest post created in the last 24 hours in laravel?
sorted by the number of posts in descending order.
If I'm not wrong, you are asking for the users with the highest number of posts created in the last 24 hrs.
To accomplish this, do the following:
$users = User::withCount(['posts' => function ($query) {
$query->where('created_at', '>=', carbon()->now()->subDay());
}])->orderBy('posts_count', 'DESC')
->get();
As the documentation states, you can add constraints to the queries.
Counting Related Models
If you want to count the number of results from a relationship without actually loading them you may use the
withCount method, which will place a {relation}_count column on
your resulting models. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
You may add the "counts" for multiple relations as well as add
constraints to the queries:
$posts = Post::withCount(['votes', 'comments' => function ($query) {
$query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;
use Carbon\Carbon;
get user id:
$minusday = Carbon::now()->subDay();
$user_id = DB::table('posts')
->select('user_id', DB::raw('count(id) as total'))
->where('created_at', '>=', $minusday)
->groupBy('user_id')
->orderBy('total','desc')
->limit(1)
->get();
In regular SQL syntax you'd need something like below:
SELECT COUNT(id), user_id
FROM posts
WHERE created_at = today
GROUP BY user_id
ORDER BY COUNT(user_id) DESC
LIMIT 1;
It gets all the posts, groups them by user_id, sorts them with the highest user_id count up top and gets the first record.
I am by no means an expert on SQL, let alone the query builder in Laravel, so someone else would probably be better at writing that.
I know that you can get the posts that were created today by using Carbon, like so:
Post::whereDate('created_at', Carbon::today())->get();
EDIT: This might work for you:
$last24h = Carbon::now()->subDay();
DB::table('posts')
->select(array(DB::raw('COUNT(id)', 'user_id')))
->where('created_at', '>=', $last24h)
->groupBy('user_id')
->orderBy('COUNT(id)', 'DESC')
->limit(1)
->get();
Be sure to include use Carbon\Carbon to be able to use Carbon.
This should give you both the amount of posts and the corresponding user id.

Foreach loop not going through correctly

I have a form to mark employees' attendance that HR fill in which is looped over every current employee. I need it to show the current values where attendance is marked in the database, otherwise it should be blank.
In my controller I query the existing results:
$results = Attendance::where('Sdate', '=', date("Y-m-d", strtotime($TheDate))) ->get();
Then loop through them to get the employee details:
foreach($results as $result)
{
$contractors = DB::table('contractors') ->where('contractors.AreaPosition', '=', $department) ->where('PRN', '!=', $result->PRN) ->get(); $employees = DB::table('current_employees') ->where('current_employees.AreaPosition', '=', $department) ->where('PRN', '!=', $result->PRN) ->get(); $showEmployees = array_merge($contractors, $employees);
}
This should exclude all employees who have a record saved in attendance for that date, however it doesn't seem to be looping correctly. It will exclude some results, but not all. If I return the results variable I get the correct list of records so I know that part is working correctly.
What I'm looking to achieve in my view is something like:
#foreach($attendance as $results)
Show form where there's an existing record for this date and department
#endforeach
#foreach($employees as $employee)
Show form for all employees in this department (but should exclude results where there is a record in attendance)
#endforeach
The problem with your code is you are saving the result in a variable and not in an array.
Your solution would be to store the data in an array
foreach($results as $result)
{
$contractors[] = DB::table('contractors') ->where('contractors.AreaPosition', '=', $department) ->where('PRN', '!=', $result->PRN) ->get(); $employees = DB::table('current_employees') ->where('current_employees.AreaPosition', '=', $department) ->where('PRN', '!=', $result->PRN) ->get(); $showEmployees = array_merge($contractors, $employees);
}
Try printing contractors array and see what happens.I hope this works
This was answered for me on Laracasts.
What I needed to do was create a list of the variable to check against (employee number)
$PRNs = Attendance::where('Sdate', '=', date("Y-m-d", strtotime($TheDate)))->lists('PRN');
And then use Laravel's whereNotIn, checking against the list.
$contractors = DB::table('contractors')
->where('contractors.AreaPosition', '=', $department)
->whereNotIn('PRN', $PRNs)
->get();

Resources