How to handle complex user status? - php

My application deals with user payments. In the company, this user has the following status:
compliant (user payed all debts so far)
overdue/default (user registered for 3 months minimum and has hasn't payed at least 1 debt)
inactive (user is registered for less than 3 months and hasn't payed any debt)
How is the best way to deal with those rules in multiple places (and rules) inside the application?
Do I need a field like status_id and a cron to update this every hour?
No status_id field and write the SQL rule in every query that needs to show the status?
Load a User model and call a ->status() method that has the rule? In this case, how can I show "totals", like: We have 3000 overdue users, 15000 inactive users etc...
This is giving me headaches for months and I really need help haha. We currently have a solution but it's too complex to deal with it. As it seems to be something common within apps that deal with payment, there's must be a simplier way to do this :P
Thanks!
Notes
Application has currently 90.000 users
We need this info in real-time.
This info is used in reports to generate chars.
This info is showed inside the user profile.
This info is showed in listings.
Users are notified when a user change between those status (like, "you have debts" when user enters in "overdue").
This info is not managed by application users.
The status need to be tracked.

If you are using this field in multiple places, then you should store the status in a single place and update it as appropriate (I would also keep a history of the statuses, but that is another matter).
If the status changes due to some user actions (such as a payment being processed), then you can use a trigger on the action. However, your status changes seem to be based on time after an event. In that case, you should run a regularly scheduled job (as a cron job or database event).
I am a bit confused about why you would do this every hour. It seems that once per day would be most appropriate. If the "debts" are paid at arbitrary times, then the process of payment should update the status. For the downgrading of the status, a single job once per day should be sufficient.

Interesting question, but also not one with a single answer.
I think the complexity here might come from the surrounding code, rather than the core business logic and requirements. I say this because three status types, all of which are derived from your internal application, isn't too bad.
One possible solution, and I am assuming some level of MVC or similar.
Given your model, user, and extending an ORM like Eloquent (I will Eloquent from Laravel because I am most familiar with it, but any ORM will work):
use Illuminate\Database\Eloquent\Model;
use App\DebtCollector;
public class User extends Model
{
// Assuming model has the following fields
// id, status, registration_date, and a one to many
// relationship with debts
protected $fillable = [
'raw_status',
'registration_date',
];
public function debts()
{
return $this->hasMany(Debt::class);
}
public function refreshStatus()
{
$dc = new DebtCollector();
// Business logic inside the "DebtCollector" class
$this->raw_status = $dc->resolveStatus($this->debts, $this->registration_date);
// Save value to the underlying datebase
$this->save();
}
// If you fetch a status directly, it will refresh first,
// then return the value
//
public function getStatusAttribute()
{
$this->refreshStatus();
return $this->raw_status;
}
}
// Schedule task somewhere - ran nightly, or whenever
//
// This way you can refresh the status only on certain groups
// of data - for example, if the business flow means that once
// they become compliant, they can't go back, there is no need
// to refresh their status anymore
//
User::where('raw_status', '<>', 'compliant')->refreshStatus();
// Alternatively, the schedule could chunk results and do an update
// only to those not updated in the last 24 hours
//
$date = new DateTime;
$date->modify('-24 hours');
$formatted_date = $date->format('Y-m-d H:i:s');
User::where('last_updated', '>', $formatted_data)->refreshStatus();

I would say there are multiple solutions to this problem.
I would suggest not having any defined status. From what I can see you can always "figure out" the current status based on some other data.
For example "user payed all debts so far". This is something you simply know just by analyzing all changes for given period. You can aggregate data to figure out all you need to know. So then you don't need to save the status at all. It is just derived from all the changes to the customer's account that happened over specific period.
Same is for totals. You can do this easily on database level or even by using some document based DBs or ElasticSearch.
This, of course, assumes you trace the history of changes. If you do - problem solved. If you don't - you have to save the status into database and will not be able to get historical data.

Related

Whats the best way to store booking information over multiple pages without DB in Laravel?

I'll try and cover most of the important details here...
I'm currently working on a booking system for a transport provider. I am using Laravel and originally started by having the Booking model attached to a User.
The client now however wants them to not have to login or register until the last step of the booking process. I have done carts etc in Session / Local Storage before but I thought before I start I would get some input from the friendly folks over at StackOverflow!
The most ideal way for me at this point would be to make the user_id on the Booking model nullable, but then when the visitor returns to the site how will I then know which booking is theirs?
I hope this makes sense & I hope someone out there has dealt with a similar problem to this one and can shed some light on the best strategy going forward!
I created something similar to what you say in a company that has been working for some time, I indicate how I proposed it, to see if it can help you achieve what you want to achieve in the most optimal way.
As you indicated, I created the reservations with the nullable user_id, as it usually happens in almost all reserve applications, they are not eternal, so I added a field (max_datetime) of maximum time that reservation would last and a field (token) with code only for the reservation, in addition to a field (ip) for the ip of the session. (In the application that I made, the reservation was maintained 12 hours maximum or until 11:59 pm on the same day, which may be less than 12 hours).
Then create a Task Scheduling for a custom Artisan Console. What it did was eliminate the reserves that fulfilled the condition to be eliminated.
When the reservation was completed it was associated with the user_id and the other fields with null (max_datetime, token, ip). Ah! Yes and a field (confirmed) to confirm with "true" that the reservation has been completed, by default to "false".
I used session to check the "ip" and the "token", and if not, I asked that if they had the token, to indicate it.
The system allowed you to obtain the "token" in case you did not want to continue at that moment, warning you of the time the reservation was kept.
I do not have access to the code since it was from a company and I only kept the idea. I hope it helps you. A cordial greeting.

Laravel: check if two users are simultaneously logged in

I want to validate the use of coupons in my app by checking if both the business' owner and the final client are the ones redeeming the coupon. This would be the process:
Client shows his QR code
Owner scans it
Coupon is redeemed if both of them are logged in (this would happen when the Owner's device makes a post request and uses CouponController)
I know I can use auth() to validate the owner's status (he wouldn't be able to access the Redeem view when logged out anyway), but is there any way to check if the Client is also logged in without modifying the User row in the database? Right now, I use the following:
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
class LoginController extends Controller
{
...
public function store()
{
if( !auth()->attempt(request(['email','password'])) ){
return back()->withErrors([
'message' => 'Please check your credentials and try again.'
]);
}
$user = User::find(auth()->id());
$user->active = 1;
$user->save();
return redirect()->home();
}
Let's first clarify on how PHP scripts work and how that impacts your path to know if a user is online or not.
Under your Laravel application, for the very reasons on how PHP processes HTTP requests, you can only identify hits (requests), that you can interpret like heartbeats. So, you cannot (at least with bare PHP) see the user online all the time, but you can assume the user is there if the page hit was recently.
That said, it will be up-to-you what will be the acceptable time window to interpret if the user is online or not, once given a page hit/http request.
If you only record that the user is active (as a boolean/int flag like $user->active = 1) upon login, you will think the user is active even long after the user is gone from the application, as the user session may perfectly remain still active (open) but the user is actually inactive.
There are many ways to go around this.
One possible approach is to remember the last time a user hit your page, so you consider him online after the next -say- 5 minutes (this value is up to you). This approach is fair enough for what you are willing to achieve. Keeping this track can be achieved with middlewares, so your controllers are kept clean.
On how exactly implement this, well, that would be an entire and opinionated git project to post here and it's probably outside the scope of this answer. Long story short, think of keeping record of timestamps of the events you will consider relevant as user activity, instead of a flag with no timing information.
If you are willing to implement this as a usable feature with external packages, here are a few options:
https://github.com/highideas/laravel-users-online
https://github.com/thomastkim/laravel-online-users
https://github.com/joshrainwater/active-users
Even if you are not willing to pull in a third party package, feel free to dig in their sources (start on the Traits) to get some ideas on how to go around this.
You will also notice that some of them use Cache to keep track of disposable data without the need of storing this into your business ERD in database.
Hope this helps as a starting point.

Check in/out in attendance system using codeigniter

I am trying to build an online attendance system where employees sign in and check in daily except for weekends and vacations.
so , my idea was to create a daily attendance record as a table in the database.
Attendance_date_daily date
Employee_ID number(auto generated)
Check_in_time time
Check_out_time time
Attendence_status varchar
I am using codeigniter v 3.0.0
it's easy to create a model to get the current time and save it in the database when the user check in/out.
but the problem is that, if the user was absent for a day or more , then the system will not create a record for those days.
moreover, i can't create the records beforehand. since i don't know when the user will have his/her vacation and working days may differ.
what is the best way to create and manage those daily records?
One possible solution may be to allow the employees to do their check-in each day normally which would populate the database for those days.
In order to add the absence records for those who have not checked in you could use CRON or something similar to schedule a task at perhaps midnight each day. Use this task to target a url that would run a method in a controller that will check employees against the daily records. Obviously for those whom have checked in no action will be performed, although for those who have not and are not marked as on vacation or not working you update the database to add the absence records.
With a system like this you would typically invoke the url to perform the update using whatever system you use with wget or something similar to 'load' the url and force it to run. A security consideration also would be that you'll want to add in a secret key as a GET parameter or something similar so that the method can check that it's being invoked via the task and not e.g. someone visiting the url by comparing the GET parameter with a stored key that you've set.

How to assign a record to a person from a pool automatically using MySQL/PHP while preventing across assignment

I have a script that is written in PHP. It uses MySQL database to store records.
Basically, I have team of users that are making random calls to a different business. I want to add list of phone number in a queue "pool table". The system will need to assign the new call to the user. Now If a user is already working on a phone call I don't want another user to start calling the same number. I need a solution to prevent 2 people having the same record assigned to them. So if phone number 000-000-0000 is assigned to the user X the same record will be skipped and the next one in line get assigned to the next available user.
This table will be accessed a lot so I need a good solution that will prevent 2 people from working on the same record and also not cause system issues.
One way I can think of but looking for a better solution is
open transaction
select a call where record status is available
update that call by changing the status from records available to record pending.
commit transaction.
If the use completed the call then updated with a status of completed otherwise make the record available again.
what are other solution available for me?
Thanks
Without a little more information about the workflow, it's hard to know what to suggest, but it sounds like users are interacting with the application somehow while they are taking calls...true??
If so, you must have some way for the user to alert the system they are ready for a call.
ie...
I just started my shift... Deal me a number.
Or...
Submit notes from last call... click submit and Deal me another number.
In this scenario, it seems like it should pretty easy to just let the users "request" the next number. You could probably just insert the users id on that record so it shows in their queue.

Need help setting up a logic behind a Facebook notification system style

I want build a notification system to my website, similar to Facebook. The notifications don't need to be in real time.
This is what I have in mind:
User create an event (upload a new photo, add a new comment, like a photo, or even an administration alert to all users)
Run a cronjob every 5 minutes to add the notifications into the notifications table: id|id_content|type_of_content_enum|affected_user_id|date|seen_bool
The cronjob, will run several functions for each type of notification, for example:
add_photos_notification() // This function will check all photos added in the past 5 minutes, and insert a row in the notification table, for each user following this person. The function will group all photos added into the past 5 minutes, so the follower don't get many notifications for the same type of content. Resulting in a notification like: User X added Y photos in his profile.
add_admin_notification() // This function will check all news added by the administration of the site in the past 5 minutes, and insert a row in the notification table, for each user on the system ...
Is this a correct approach to build a notification system?
Is it possible to miss an event, running a cron every 5 minutes where functions retrieve the past 5 minutes events?
To be safe, do you think an alternative could be checking all events where a field 'notified' is not true? The same function that will grab the events where 'notified' = false, update it to true after adding that notification into the notifications table.
Thanks,
I went with the cronjob route and working good so far. Since our system got so many users to be notified, I found it the most appropriate way to do for two reasons.
I don't need to edit my current scripts code, inserting functions to add notifications for every event I want notify.
Since there gonna be some actions where many users are affected, adding notifications in real time could result in long script delays and time outs.
I built a class called notifications and inside this class, there are functions to add notification for every event I want notify, for example: user_added_a_new_photo(); user_commented_on_a_photo();
For every notification generate, Im adding 1 entry per user to be notified. This is how my notifications db looks like:
id
affected_user_id //user being notified
user_generating_the_notification_id
content_type // enum containing all type of notifications my system has (example: photo, video, comment) ...
content_json // a json containing the notification content. Based on the content type, on the display view file, I call helpers that will format the notification row using the json info.
date // the date the notification was added
seen_on // the date the user saw the notification
clicked_on // if user clicked on the notification, store the date he clicked on it
display // true or false
For this purpose, I added the display field cause for every new notification I create, I check the database if the same user, has another not seen notification, from the same generating user. If this condition is true, I set the old notification to display = false, and group the two new notifications resulting in something like: User X added X new photos in his gallery.
The clicked_on field, stores the date the item was clicked so I can generate reports based on this info if I need to. When displaying the content, if this item is not null, I highlight the notification to mark those not checked yet.
I created a text field to store the notification content in json, cause different notifications has different styles to present the users. For example, a new comment notification, has only texts, but a new photo notification, has a preview thumb.
So far, no issue running it and working for my needs.
The only downside, since the cronjobs can only be run only every 1 minute, the notifications may have 1 minute delay. But since I don't need it in real time, I set the cronjob to run every 5 minutes.
I've been looking into something like this as well and i just found this question on stackoverflow
Building a notification system
Have a look at the answer, the user does shed a fair bit of light on the theory of how to implement this sort of system.
As far as i can see you would need to create a record or object for each notification, if 100 people are subscribe to that event then 100 records will be generated. When a page is loaded your system will find all the notifications that correspond to the user logged in (Maybe by record ID) and then you would notify the user of how many notifications they have.
Now another way of doing this (Note i haven't implemented any of these, they are just ideas) would be to make a notification in a a table, then we'd have a second table that will hold a users ID and a notification ID, a user will be added to this table when they are subscribed to a notification, for example, confirmation of a friend request.
Please note again, these aren't proven methods, they're a result of some of my research of the matter, i'd read the post i gave you before doing anything.

Categories