My scenario is:
PHP Script 1:
$user = User::find(1);
$user->email = 'email#emailness.com';
$user->save();
PHP Script 2:
$user = User::find(1);
while ($user->email != 'email#emailness.com') {
/** Do stuff **/
}
Now my question is, does the email variable get updated when it's updated from another script? For example, Script 2 runs, while it runs, Script 1 also runs. Will the while statement update and move since the condition isn't true anymore?
I think the simplest way to achieve something like this is to run a query every minute or so checking for updated columns. The alternative would be to make your daemon run some sort of server which gets pinged by a model event.
If you only need to monitor a single user's email, it's easy enough - User::find($id); then sleep(60). If you need to monitor more than one user it gets a bit trickier.
If your model uses timestamps (created_at and updated_at), it's possible to query only for models that have been updated recently. Let's say you want to re-query the database every 60 seconds - what we want to do is query all User models that have an updated_at greater than 60 seconds ago.
I'll use the Carbon class, which is an extension of DateTime included with Laravel, but you can also construct a normal DateTime object or a datetime string manually.
$dt = Carbon\Carbon::now()->subSeconds(60);
$users = User::where('updated_at', '>=', $dt)
->get();
foreach ($users as $user) {
// do something with users that have been updated
}
sleep(60);
You can also replace get() with lists('email') if you only want a flat array of emails of updated users.
There may also be more efficient ways than using sleep(60), maybe using Symfony's Process class, but I'll not get into that here.
Related
I am using the Laravel 5.7 and now I am facing the following problem:
I have a cronjobs table which contains many cronjob records. Let's take one for example:
Record one should be repeated every 50 minutes.
I created function getMinutelyCronjobs fetches from DB all the cronjobs whish has to be executed minutely.
I went this way: I created a class in Commands Minutely.php where I get all this cronjobs in the handle() function. When I initialized all the data in Minutely.php class, I cann call this handle() function using $schedule->command('cronjobs:minutely')->everyMinute();.
The problem is, that every cronjob could have different minute_x(recurrence, in my example 50) and then the $schedule->command('cronjobs:minutely')->everyMinute(); should be $schedule->command('cronjobs:minutely')->everyMinute(50);.
here is my getMinutelyCronjobs function :
public static function getMinutelyCronjobs(){
$fields = ['id', 'protocol', 'script', 'minutes_frequency', 'login_user', 'login_password', 'email'];
return Cronjob::select($fields)->where('minutely', 1)
->orderBy('id', 'desc')
->get();
}
How could I implement this ? Thank you for your help.
If you can get by with running the task every hour instead of every 50 minutes, the first answer will work. If you need it to be specifically every 50 minutes, you can try something like this, which should run every 50 minutes instead of every 60.
EDIT:
You didn't give the details about the data you are pulling from your database relating to the jobs, but you should be able to just do something like this to schedule each job.
foreach ($jobs as $job) {
$schedule->command($job->command)->everyMinute()->when(function () {
$now = new \DateTime;
$minutes = floatval($now->format('G'))*60 + floatval($now->format('i'));
return $minutes % $job->minutes == 0;
});
}
everyMinute() instead of use hourlyAt() method
Run the task every hour at 50 mins past the hour
$schedule->command('cronjobs:minutely')->hourlyAt(50);
for more information read this article
I'm currently creating a basic private messaging system in Laravel. In my database I have a messages table. Two of the columns are "sender_id" and "recipient_id".
What I am trying to do is use eloquent to go through this table and list in the inbox all the times the current logged in user's id is listed in these columns. I will then use "distinct" to ignore duplicate entries so that the user is only listed once. Using this ID, I can display the username of the recipient so that when the user clicks on the username, it will take them to the message thread for that user.
For the sake of testing, I am only currently retrieving the column "recipient_id" and outputting it into the blade. Here is the line I believe should only output the recipient once despite being found many times in the table.
MessageController
$messages = Message::where('recipient_id', $user)->distinct('recipient_id')->get();
However in the blade, the same user is being output twice (as they have 2 messages found) when I would have expected the distinct function to remove the duplicate entry.
I have also tried
$messages = Message::where('recipient_id', $user)->distinct()->get();
But that also did not work.
If I have received 3 messages from 2 different users, it is outputting something like below:
Messages from:
Example User 1
Example User 1
Example User 2
What it should output
Example User
Example User 2
Thanks
Use Laravel Collection and changed code to:
$messages = collect(Message::where('recipient_id', $user)->get());
$messagesUnique = $messages->unique('recipient_id');
$messagesUnique->values()->all();
You can follow this
$payment = Payment::with('invoice','customer')->get();
$payment = $payment->unique('customer_id');
It will return all the unique customer payment data.
You should use something like this:
$data = Message::select([ DB::raw('DISTINCT(recipient_id)')'])
->where('recipient_id', $user)
->get();
instead of using a collection, as when you use collection you returned with all data you needed or not then you filter it. but when using distinct in the query you just take them in one process.
Or you can try one of these options: mentioned here
$diff = Crud::distinct()->pluck('name');
$diff = Crud::distinct()->get(['name']);
$diff = Crud::distinct()->select('name')->get();
What I am trying to do
I want to query a specific set of records using active model like so
$jobModel = Jobs::find()->select('JOB_CODE')->distinct()->where(['DEPT_ID'=>$dept_id])->all();
Then I want to assign a flag attribute to the records in this activerecord based on whether they appear in a relationship table
What I have tried
So in my job model, I have declared a new attribute inAccount. Then I added this function in the job model that sets the inAccount flag to -1 or 0 based on whether a record is found in the relationship table with the specified account_id
public function assignInAccount($account_id){
if(JobCodeAccounts::find()->where(['JOB_CODE'=>$this->JOB_CODE])->andWhere(['ACCOUNT_ID'=>$account_id])->one() == null){
$this->inAccount=0;
}
else{
$this->inAccount = -1;
}
}
What I have been doing is assigning each value individually using foreach like so
foreach($jobModel as $job){
$job->assignInAccount($account_id);
}
However, this is obviously very slow because if I have a large number of records in $jobModel, and each one makes a db query in assignInAccount() this could obviously take some time if the db is slow.
What I am looking for
I am wondering if there is a more efficient way to do this, so that I can assign inAccount to all job records at once. I considered using afterFind() but I don't think this would work as I need to specify a specific parameter. I am wondering if there is a way I can pass in an entire model (or at least array of models/model-attributes and then do all the assignations running only a single query.
I should mention that I do need to end up with the original $jobModel activerecord as well
Thanks to scaisEdge's answer I was able to come up with an alternative solution, first finding the array of jobs that need to be flagged like so:
$inAccountJobs = array_column(Yii::$app->db->createCommand('Select * from job_code_accounts where ACCOUNT_ID = :account_id')
->bindValues([':account_id' => $account_id])->queryAll(), 'JOB_CODE');
and then checking each job record to see if it appears in this array like so
foreach($jobModel as $job){
if(in_array($job->JOB_CODE, $inAccountJobs))
$job->inAccount = -1;
else
$job->inAccount = 0;
}
Does seem to be noticeably faster as it requires only a single query.
I'm asking this question in order to find the best practice to do it.
DB::table('owners')
->where('property_id',$id)
->update(array('person_id'=>$owner));
The problem is that in the table owners might not have a row to update. In that occasion i need to make an INSERT INTO instead of UPDATE.
My problem is that i have to run 2 queries each time, one for checking if the row already exists, and one more to update or insert into. Is it right to run 2 queries each time? Is there a better way to achieve that? I need to keep the queering processes fast for the user.
UPDATE: The table owners is a middle table of a many to many relationship. Unfortunately i cannot use ON DUPLICATE KEY.
well you could try to use firstOrCreate method of Laravel to check if user exists. After that retrieve the user object and pass it to an update function else if the user is not found firstOrCreate method will take care of you as it will create a new user with the data you will provide and will auto increment last user + 1 id.
There is also the option to use firstOrNew which will check if an instance exists based on the array values you passed and if no match is found it will auto create a new instance of the model you are handling for further manipulation.
Here is example with firstOrNew
Example Controller file.
public function getFirstUserOrNew($email)
{
$user = User::firstOrNew(['email' => $email]);
if($user)
{
$this->UpdateUser($user);
}
else
{
$this->CreateUser($user);
}
}
public function UpdateUser(User $user)
{
//Do update stuff
}
public function CreateUser(User $user)
{
//Do create stuff
}
P.S - I'm from Greece, if you want to discuss anything in native language send me a PM :)
EDIT:
Thanks to #Pyton contribution It seems you can also use an updateOrCreate method as it is explained here.
If you want to Update or Insert row You can use updateOrCreate
$owner = Owner::updateOrCreate(['property_id' => $id], ['person_id'=>$owner]);
In my symfony project, I have a "complex" query that looks like:
$d = Doctrine_Core::getTable('MAIN_TABLE')
// Create the base query with some keywords
->luceneSearch($keywords)
->innerJoin('w.T1 ws')
->innerJoin('ws.T2 s')
->innerJoin('w.T3 piv')
->innerJoin('piv.T4 per')
->innerJoin('w.T5 st')
...
->innerJoin('doc.T12 docT')
->innerJoin('w.Lang lng')
->execute();
I added all those innerJoin to reduce the number of query due to my data model. Actually all data are recovered with this only query.... but the query took from 2 to 20 sec. depends on keywords.
I decided to use memcache because data are not changing all the time.
What I've done is configuring memcache and adding
...
->useResultCache(true)
->execute();
to my query.
What is strange is that :
The first time (when the cache is empty/flushed), only one query is execute
The second time, ~130 ares executed and it take more time than the first...
Those "new" queries are retrieving data from "inner join" for each record.
What I don't undestand is why "innerjoined" data are not saved in the cache?
I tried to change the hydrate mode but it seems not to be influent.
Someone has an idea?
After a whole day to googlise, to analyse doctrine and become desperate, I found an article that explain the solution:
class User extends BaseUser{
public function serializeReferences($bool=null)
{
return true;
}
}
The problem was the profile object was not getting stored in the result cache and thus causing a query each time it was called from the user object. After much hunting around, a long time in #doctrine, and a few leads from a couple of people, it turns out, by default, Doctrine will only serialize the immediate relation to the main object. However, you can make it so that it will serialize objects further down the line by overriding the function serializeReferences to return true in the class you want to serialize references from. In my example this is the User class. Since our application will never only need the ‘User’ class to be serialized on a result cache I completely overrode the function and made it always return true
http://shout.setfive.com/2010/04/28/using-doctrine-result-cache-with-two-deep-relations/