Laravel - app with 'Calendar' view & attendance of users approach - php

I've got 'behavioral' problem with approach to Attendance system.
I have database like this:
Users:
- increments: id,
- string: name,
- string: email,
Attendance:
- increments: id,
- integer: user_id,
- string: type,
- date: start,
- date: end,
I want to print it in table, where thead is an iteration of days of the month(I'm using for loop and iterate through days in month), 1st column in 2nd is an User name, and rest of columns in this row are the attendance "things" like box with different color, depends of attendance type.
My question is that: what's the best approach? Store attendance like I do or 'divide' the range to single attendance and do like this:
Attendance:
- increments: id,
- integer: user_id,
- string: type,
- date: date_of_attendance.
I've got issue with displaying it (User model hasMany Attendances), and I do not have an idea how to simplify it.
Should I use 'inside model' function to retrieve the data and parse it before passing it to view like (User model):
public function filterAttendance()
{
return $this->attendance()->whereBetween('start', [Carbon::now()->firstOfMonth(), Carbon::now()->lastOfMonth()])->orWhereBetween('end', [Carbon::now()->firstOfMonth(), Carbon::now()->lastOfMonth()])->orderBy('start', 'ASC')->get();
}
In view I do foreach through all $users, then inside it foreach through all $monthDates(parsed dates to Carbon from 1st of month to last) and then I try to compare the days of attendance if they in "specific" range.
Also I want to achieve best computational complexity in this example.

Related

Laravel Eloquent / Carbon between two dates

Some background first, I have a contact model to keep track of a user's contacts, and I have an anniversary model, they are linked as Many-to-Many with a pivot table (Each user can decide his/her own anniversaries and then add those to a contact, so the anniversaries are not hard coded).
So each User hasMany Anniversaries and every Contact can have many Anniversaries, while an Anniversary can have many Contacts.
For example, to get upcoming birthdays for all contacts for the next x months, I use the following query:
auth()->user()->contacts()->whereBetween(DB::raw('DATE_FORMAT(birthdate, "%m-%d")'), array(
Carbon::now()->format('m-d'),
Carbon::now()->addMonths($months)->format('m-d')))
->get();
This works perfectly. However, for the anniversaries it is a bit more complicated. An anniversary at this moment can have a frequency of monthly, quarterly, semi-annually and annually. This is stored as a string in the pivot table anniversary_contact.
I would like to use a similar query for this as the above one, but I am wondering if this is at all possible without having to repeat a lot of orWhereBetween like below:
In the below query I check for upcoming anniversaries with a frequency of monthly for the next 3 days:
$anniversaries = auth()->user()->contacts()->get()->each(function ($contact)
{
$contact->anniversaries->each(function ($anniversary) {
return $anniversary->whereBetween(DB::raw('DATE_FORMAT(date, "%m-%d")'), array(
Carbon::now()->format('m-d'),
Carbon::now()->addDays(3)->format('m-d')))
->orWhereBetween(DB::raw('DATE_FORMAT(date, "%m-%d")'), array(
Carbon::now()->addMonths(1)->format('m-d'),
Carbon::now()->addDays(3)->format('m-d')))
->get();
// Left the rest of the 10 "orWhereBetween" out of it in this example
});
});
Although the above repetition is a seperate issue, my main issue is this:
DB::raw('DATE_FORMAT(date, "%m-%d")')
There is no date field in the anniversaries table, it is in the pivot table.
Main question: How to use the above query to get the results I need, from checking against the pivot date?
Bonus question: Can the above query be made "simpler" or "smaller" without having to use a lot of orWhereBetween statements? If I need to check an upcoming anniversary that is a monthly one, I will have 12 orWhereBetween statements. Can this be reduced?
Or should I forget about checking it on the database level and instead get all anniversaries and use Carbon to check against upcoming anniversaries?

Laravel 5.5 - get data from model where realated model has no data in specific month

I am trying to get some data from a specific model, where the related model has no data in the current month.
Something like
Select * FROM houses
JOIN customers
WHERE "there is no customer for the current month;
But in Laravel.
I tried multiple things out but couldn't do the trick.
I have a house table.
The house table has many customers.
Now I want to get all houses where there is no customer for the current Month.
I tried something like:
House::whereMonth('House.customers.created_at', Carbon::now()->format('m'))->get();
One solution that worked for me is:
$houses= House::all();
$customers = Customer::whereNotExists(function($query) use ($houses){
$query->whereMonth('created_at', Carbon::now()->format('m'));
})->get();
Does somebody have a more cleaner way?
SQL Table structure:
customer:
'id', 'house_id', 'name', 'created_at','updated_at'
houses:
'id', 'name', 'created_at','updated_at'
Now I want to get all houses that has no customers for the current month
you can use whereHas like that
House::whereHas('consumers' , function($query){
$query->whereMonth('created_at' , '<>' , Carbon::now()->format('m'));
})->get();

How to make sales report more "scalable"?

I built an application to keep track of the sales. In my customers view, I want a column with total sales per customer, but as the customer base is growing, the list is loading more and more slowly. This is what I did (simplified):
Controller:
$customers = App\Customer::get();
View:
#foreach ($customers as $customer)
{{ $customer->name }} {{ $customer->totalSales() }}
#endforeach
Model:
public function totalSales()
{
$invoiceLines = InvoiceLine::whereHas('invoice', function ($query) {
$query->where('customer_id', $this->id);
})->get();
$sales = $invoiceLines->reduce(function ($carry, $invoiceLine) {
return $carry + ($invoiceLine->quantity * $invoiceLine->pricePerUnit);
});
return $sales ?: 0;
}
What would be the best way to make this view/report more "scalable"?
I have been thinking in creating command that calculates the total sales per customer overnight and put the result in the customer table, but that means that the numbers won't be accurate during the day...
this seems like a very interesting problem.
I have been thinking in creating command that calculates the total
sales per customer overnight and put the result in the customer table
this is a good option.
but that means that the numbers won't be accurate during the day...
You can keep the numbers accurate by doing the following:
by incrementing the customers table count every time a invoice is made.
This should work for total sales.
Make sure you have an index on the customer_id column.
Search for ways to do a "SQL SUM on 2 columns using laravel".
Try and find some way to do "SQL SUM on 2 with a GROUP BY. Doing this will replace #2
A good way to speed up your application is to avoid making calls to the database in a loop. That is what #3 is suggesting (the loop in this case is the #foreach in your View and the database call is the InvoiceLine::...->get(); in totalSales()
Adding the index (if missing) and reducing the # of calls to the DB will yield the best results.
I have limited knowledge of Laravel but one way to do this with raw SQL would be:
SELECT c.name, ts.totalSales
FROM customer c
INNER JOIN (
SELECT customer_id, SUM(quantity * pricePerUnit) as totalSales
FROM invoice
GROUP BY customer_id
) ts ON c.id = ts.customer_id
You can see how all the data you're trying to print is pulled at once? I assume you'd want to try and write that using Laravel's Eloquent thingy.
Based on the answers above, I came to the following solution:
I created an event: App\Events\InvoiceSaved which is dispatched every time an invoice is "touched" (created, updated or deleted). The App\Events\InvoiceSaved event will calculate the total sales for the customer and add the result to the customers table (extra field total_sales). Now I can just query my customers table and need to query a relation. The loading time dropped from 7 seconds to 0.5 second!

Good way to map table based on members and months in year

I need to make simple table like this
This table represents yearly membership status for each month, it's super simple there is no advanced stuff there, that membership status is updated manually by admin (when member pays, admin update status for that month).
From database standpoint i have 2 simple tables like this:
As you can see i have membership table with member_id and date, and when payment is made that table is being populated.
So my question is: what is good way to map data to match that table ?
i'm thinking something like this (for example i want all members data from 2017 year)
select m.*,
GROUP_CONCAT(MONTH(ms.date)) as dates
from members m
left join membership ms on m.id = ms.member_id
where ms.date >= 01-01-2018
and ms.date <= 12-31-2018
this query should return list of members with dates array(array of numbers, each number represents month), so on UI side i could maybe generate this table view based on that ? Any suggestions for this, anyone have better solution ?
Your query seems fine. You can then use it to determine whether or not a month exists in the array of dates by using the includes function on the date array.
For example, to check if there should be an X for February you would check dates.includes(2). If true, place an X, otherwise leave blank.

How to fetch data of one table column into another table row

I have 7 tables one is faculty_subjects rest are Monday,Tuesday,Wednesday,Thursday,Friday and Saturday my tables structure is like this
i have attached image url
http://s27.postimg.org/434y3255f/School_Management_System.jpg
i tried to make whole design here but everything mashedup
live static page http://www.school.eptins.com/
when someone select class and section relevent to that subjects and faculty display in fields.
I think time-table for one class is a table in itself. Instead of scattering time-table information of a classroom over 6 different tables, you could keep a table name timetable_IA and that table could have first column for periods (Period I, Period II, Period II, ...) and second column for subjects on Monday, third column for subjects on Tuesday, etc.
That way, when a person chooses class IA, you have to just gather information from one table only.
For the second table (Subject Faculty), you could make an array of subjects appearing in time-table by reading time-table of IA, and then call in names of faculty teaching those subjects and construct the table.

Categories