Laravel observer add delay - php

How can I add delay to laravel observer before it execute function?
I have this code but it doesn't work:
public function created(School $school)
{
$addTime = Carbon::parse($school->created_at)
->addSeconds(6);
if(Carbon::now() = $addTime) {
$manager = School::where('id', $school->id)->with('manager')->first();
$user = User::where('id', $school->manager->id)->first();
Mail::to($user->email)->locale('id')->queue(new SchoolGenerated($school, $user));
}
}
Logic
I have such function in my controller
public function store(Request $request) {
$school = new School;
//.....
if($school->save()){
//save manager.....
}
}
As you see I assign manager to school after school data been saved therefore if I run my observe immediately It won't find my user (manager) so I need to put delay into my observer till manager data stored as well.
How can I add delay to my observer?

you can use sleep(int $seconds ) function in php, try this:
public function created(School $school)
{
// add delay
$seconds = 10;
sleep($seconds);
$manager = School::where('id', $school->id)->with('manager')->first();
$user = User::where('id', $school->manager->id)->first();
Mail::to($user->email)->locale('id')->queue(new SchoolGenerated($school, $user));
}
}

Solved
As there was no way for me to get school data after manager being created so I've changed my observe to rule on manager creation instead of school creation that way I can get manager data and based on manager data get school data. So I just basically rotate the logic 180˚ and got my desire result.

Related

Why model/variable keeps its value?

I have problem with my code in Laravel Framework. What I am trying to do is sell method inside model. The problem is that $variable keeps its value untill next code execution. How I should make it so it's gonna work like I want to?
/// Model method.
public function Sell()
{
$this->UserData->increment('gold', ($this->ItemData->price / 100) * 80);
$this->delete();
return null;
}
///in controller
$user = \Auth::user();
$user_item = UserItems::find(10);
$user_item->Sell();
return $user_item->ItemData->name; /// Returns full data about model even if I deleted it/sold. After next refresh, returns null/error.I want it to return null since beginning.
Even if you have deleted a record from a database by $this->delete(), $user_item variable still holds a reference to the filled model until the script ends or you destroy the variable.
It seems that you want to return a result of the sell() function. In your case it would be
///in controller
$user = \Auth::user();
$user_item = UserItems::find(10);
$user_item->Sell();
return $user_item->Sell();
I don't know what you are trying to do but below code will solve your issue -
/// Model method.
public function Sell()
{
$this->UserData->increment('gold', ($this->ItemData->price / 100) * 80);
$this->delete();
$this->ItemData = $this->ItemData->fresh()
return null;
}
///in controller
$user = \Auth::user();
$user_item = UserItems::find(10);
$user_item->Sell();
return $user_item->ItemData->name; /// Returns full data about model even if I deleted it/sold. After next refresh, returns null/error.I want it to return null since beginning.

Laravel Relationship availability after save()

I'm building a simple timetracking App using Laravel 5 where I have two Laravel models, one called "User" and one called "Week"
The relationships are pretty simple:
Week.php:
public function user()
{
return $this->belongsTo('App\User');
}
User.php:
function weeks()
{
return $this->hasMany('App\Week');
}
Now, the User.php file also has a simple helper / novelty function called "getCurrentWeek()" that, as the name suggests, returns the current week
function getCurrentWeek()
{
$possibleWeek = $this->weeks->sortByDesc('starts')->all();
if(count($possibleWeek) != 0)
{
return $possibleWeek[0];
}
else
{
return null;
}
}
My problem is: If I create the very first week for a user and attach / relate it to the user like so:
$week = new Week;
//Omitted: Setting so attributes here ...
$week->user_id = $user->id;
$weeks->save()
$user->weeks()->save($week);
And then call the $user->getCurrentWeek(); method, that method returns null, although a new week has been created, is related to the user and has been saved to the database. In my mind, the expected behaviour would be for getCurrentWeek() to return the newly created week.
What am I misunderstanding about Eloquent here / doing just plain wrong?
Relationship attributes are lazy loaded the first time they are accessed. Once loaded, they are not automatically refreshed with records that are added or removed from the relationship.
Since your getCurrentWeek() function uses the relationship attribute, this code will work:
$week = new Week;
// setup week ...
$user->weeks()->save($week);
// $user->weeks attribute has not been accessed yet, so it will be loaded
// by the first access inside the getCurrentWeek method
dd($user->getCurrentWeek());
But, this code will not work:
// accessing $user->weeks lazy loads the relationship
echo count($user->weeks);
// relate a new record
$week = new Week;
// setup week ...
$user->weeks()->save($week);
// $user->weeks inside the method will not contain the newly related record,
// as it has already been lazy loaded from the access above.
dd($user->getCurrentWeek());
You can either modify your getCurrentWeek() method to use the relationship method ($this->weeks()) instead of the attribute ($this->weeks), which will always hit the database, or you can reload the relationship (using the load() method) after adding or removing records.
Change the getCurrentWeek() method to use the relationship method weeks() (updated method provided by #Bjorn)
function getCurrentWeek()
{
return $this->weeks()->orderBy('starts', 'desc')->first();
}
Or, refresh the relationship using the load() method:
// accessing $user->weeks lazy loads the relationship
echo count($user->weeks);
// relate a new record
$week = new Week;
// setup week ...
$user->weeks()->save($week);
// reload the weeks relationship attribute
$user->load('weeks');
// this will now work since $user->weeks was reloaded by the load() method
dd($user->getCurrentWeek());
Just to add to #patricus answer...
After save/create/update, you can also empty all the cached relation data like so:
$user->setRelations([]);
Or selectively:
$user->setRelation('weeks',[]);
After that, data will be lazy loaded again only when needed:
$user->weeks
That way you can continue using lazy loading.
$user->weeks()->save($week); is not needed because you manually attached the week to the user by using $week->user_id = $user->id; and saving it.
You could actually rewrite the whole function to:
function getCurrentWeek()
{
return $this->weeks->sortByDesc('starts')->first();
}
Or
function getCurrentWeek()
{
return $this->weeks()->orderBy('starts', 'desc')->first();
}
Edit:
I made a little proof of concept and it works fine like this:
App\User.php
function weeks()
{
return $this->hasMany('App\Week');
}
function getCurrentWeek()
{
return $this->weeks->sortByDesc('starts')->first();
}
App\Week.php
public function user()
{
return $this->belongsTo('App\User');
}
routes/web.php or App/Http/routes.php
Route::get('poc', function () {
$user = App\User::find(1);
$week = new App\Week;
$week->user_id = $user->id;
$week->starts = Carbon\Carbon::now();
$week->save();
return $user->getCurrentWeek();
});
Result:
{
id: 1,
user_id: "1",
starts: "2016-09-15 21:42:19",
created_at: "2016-09-15 21:42:19",
updated_at: "2016-09-15 21:42:19"
}

Save attribute of model in database in PHP in OctoberCMS

Hi I have problem when i tried to save attribute of model to database. I write in OctoberCMS and i have this function:
public function findActualNewsletter()
{
$actualNewsletter = Newsletter::where('status_id', '=', NewsletterStatus::getSentNowStatus())->first();
if (!$actualNewsletter) {
$actualNewsletter = Newsletter::where('send_at', '<=', date('Y-m-d'))->where('status_id', NewsletterStatus::getUnsentStatus())->first();
$actualNewsletter->status_id = NewsletterStatus::getSentNowStatus();
dd($actualNewsletter);
}
return $actualNewsletter;
}
getSentNowStatus()=2;
getUnsentStatus()=1;
dd($actualNewsletter) in my if statement show that status_id = 2 But in database i still have 1. I used this function in afterSave() so i dont need:
$actualNewsletter->status_id = NewsletterStatus::getSentNowStatus();
$actualNewsletter->save();
becosue i have error then i use save in save.
Of course i filled table $fillable =['status_id']. And now i dont know why its not save in database when it go to my if. Maybe someone see my mistake?
If you are trying to modify the model based on some custom logic and then save it, the best place to put it is in the beforeSave() method of the model. To access the current model being saved, just use $this. Below is an example of the beforeSave() method being used to modify the attributes of a model before it gets saved to the database:
public function beforeSave() {
$user = BackendAuth::getUser();
$this->backend_user_id = $user->id;
// Handle archiving
if ($this->is_archived && !$this->archived_at) {
$this->archived_at = Carbon\Carbon::now()->toDateTimeString();
}
// Handle publishing
if ($this->is_published && !$this->published_at) {
$this->published_at = Carbon\Carbon::now()->toDateTimeString();
}
// Handle unarchiving
if ($this->archived_at && !$this->is_archived) {
$this->archived_at = null;
}
// Handle unpublishing, only allowed when no responses have been recorded against the form
if ($this->published_at && !$this->is_published) {
if (is_null($this->responses) || $this->responses->isEmpty()) {
$this->published_at = null;
}
}
}
You don't have to run $this->save() or anything like that. Simply modifying the model's attributes in the beforeSave() method will accomplish what you desire.

Create an Event Listener to update another database table

this is my first question so please bear with me.
How can I implement a postPersist Event Listener to update a log table when creating or updating an order in the order table using Sonata.
I understand how to use a prePersist to add information to the same database table as soon as I create a new order. (See the following code snippet)
public function prePersist(LifecycleEventArgs $args)
{
$order = $args->getEntity();
if ($order instanceof PmodOrder) {
$user = $this->serviceContainer->get('security.token_storage')->getToken()->getUser();
if ($user) {
$order->setCreatedBy($user);
$order->setCreatedAt(new \DateTime(date('Y-m-d H:i:s')));
}
}
}
But I don't fully understand how I would do this when updating another table, because it is not the same entity.
The moment an order is created, (I think) a postPersist should update another table with that order's ID and some extra information.
I think something between the lines like this;
public function postPersist(LifecycleEventArgs $args)
{
$log = $args->getEntity();
if ($log instanceof PmodLog) {
$order = ....;
$user = $this->serviceContainer->get('security.token_storage')->getToken()->getUser();
$department = $this->serviceContainer->get('security.token_storage')->getToken()->getUser()->getDepartment();
if ($order) {
$log->setOrder($order);
$log->setCreatedBy($user);
$log->setCreatedAt(new \DateTime(date('Y-m-d H:i:s')));
$log->setDepartment($department);
$log->setAction("created");
}
}
}
I don't get how to get the current order I'm busy with. And how the setAction will be something else when the user modified the order. For example 'edited' or 'approved'. I've been trough the documentation of Sonata with no luck unless I miss read something.
Remember I use Sonata, otherwise this would've been easy to implement in my own Controller Actions.
You can directly add to your entity a listener that create/update your order's logs.
First you create the listener class :
use Doctrine\ORM\Event\LifecycleEventArgs;
class OrderListener
{
public function postPersist(Order $order, LifecycleEventArgs $event)
{
// for example
// if you want to store the date creation :
if($order->getId() == null)
{
$order->setDateCreate(new \DateTime('now'));
}
// if you want to store the last update date :
$order->setDateUpdate(new \DateTime('now'));
//... or whatever you want to store...
}
}
Then register it in a service.yml :
order_listener:
class: YOUR\NAMESPACE\OrderListener
tags:
- { name: doctrine.orm.entity_listener }
Finally, link your entity to the listener (here with annotations) :
/**
* #ORM\EntityListener("YOUR\NAMESPACE\OrderListener")
*/
class Order
{
...
}

Laravel detect if there is a new item in an array

I want to implement a system in my project that "alerts" users when there is a new comment on one of their posts.
I currently query all comments on the posts from the logged in user and put everything in an array and send it to my view.
Now my goal is to make an alert icon or something when there is a new item in this array. It doesn't have to be live with ajax just on page load is already good :)
So I've made a function in my UsersController where I get the comments here's my code
public function getProfileNotifications()
{
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
//comments
if (!empty($projects)) {
foreach ($projects as $project) {
$comments_collection[] = $project->comments;
}
}
if (!empty($comments_collection)) {
$comments = array_collapse($comments_collection);
foreach($comments as $com)
{
if ($com->from_user != Auth::user()->id) {
$ofdate = $com->created_at;
$commentdate = date("d M", strtotime($ofdate));
$comarr[] = array(
'date' => $ofdate,
$commentdate,User::find($com->from_user)->name,
User::find($com->from_user)->email,
Project::find($com->on_projects)->title,
$com->on_projects,
$com->body,
Project::find($com->on_projects)->file_name,
User::find($com->from_user)->file_name
);
}
}
} else {
$comarr = "";
}
}
Is there a way I can check on page load if there are new items in the array? Like keep a count and then do a new count and subtract the previous count from the new one?
Is this even a good way to apprach this?
Many thanks in advance! Any help is appreciated.
EDIT
so I added a field unread to my table and I try to count the number of unreads in my comments array like this:
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
//comments
if (!empty($projects)) {
foreach ($projects as $project) {
$comments_collection[] = $project->comments;
}
}
$unreads = $comments_collection->where('unread', 1);
dd($unreads->count());
But i get this error:
Call to a member function where() on array
Anyone any idea how I can fix this?
The "standard" way of doing this is to track whether the comment owner has "read" the comment. You can do that fairly easily by adding a "unread" (or something equivalent) flag.
When you build your models, you should define all their relationships so that stuff like this becomes relatively easy.
If you do not have relationships, you need to define something like the following:
In User
public function projects()
{
return $this->hasMany('App\Models\Project');
}
In Project
public function comments()
{
return $this->hasMany('App\Models\Comment');
}
Once you hav ethose relationshipt, you can do the following. Add filtering as you see fit.
$count = $user->projects()
->comments()
->where('unread', true)
->count();
This is then the number you display to the user. When they perform an action you think means they've acknowledged the comment, you dispatch an asynchronous request to mark the comment as read. A REST-ish way to do this might look something like the following:
Javascript, using JQuery:
jQuery.ajax( '/users/{userId}/projects/{projectId}/comments/{commentId}', {
method: 'patch'
dataType: 'json',
data: {
'unread': false
}
})
PHP, in patch method:
$comment = Comment::find($commentId);
$comment->update($patchData);
Keep in mind you can use Laravel's RESTful Resource Controllers to provide this behavior.
try this
$unreads = $project->comments()->where('unread', 1);
dd($unreads->count());
EDIT
My be Has Many Through relation will fit your needs
User.php
public function comments()
{
return $this->hasManyTrough('App\Project', 'App\Comment');
}
Project.php
public function comments()
{
return $this->hasMany('App\Comment');
}
then you can access comments from user directly
$user->comments()->where('unread', 1)->count();
or I recommend you define hasUnreadComments method in User
public function hasUnreadComments()
{
$return (bool) $this->comments()->where('unread', 1)->count();
}
P.S.
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
this code is horrible, this way much better
$projects = Auth::user()->projects;

Categories