I have a variable, called $applicants and it contains data from users table and other tables from eager load.
Something like this:
Then I pass that variable to a Laravel Notification class via __construct method. The problem is if I dd the $applicant in the __construct method, the data is preserved, but if I dd it in the toMail method, it only contains data from user table.
Here is the code:
class DailyReportWasGenerated extends Notification implements ShouldQueue {
use Queueable;
private $applicants;
/**
* Create a new notification instance.
*
* #param $applicants
*/
public function __construct($applicants)
{
$this->applicants = $applicants;
dd($this->applicants->toArray());
}
public function toMail($notifiable)
{
dd($this->applicants->toArray());
}
I found the reason here: Relationship not being passed to notification?
So my solution is just convert my model collection to an array.
Related
I have a function that only accepts an Eloquent Collection. This is okay, but really I'd like to specify that it can only accept collections of a certain model. Is it possible to specify that we only want a collection of a certain type to function arguments?
This is what I have now:
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
/**
* #param EloquentCollection $companies
* #return array
*/
public function transformCompanies(EloquentCollection $companies)
{
$companies->each(function($company){
// do things with the company object..
});
}
The problem I see with it is that it accepts any type of collection. Could I do something like public function transformCompanies(CompaniesCollection $companies) or how does that work? What would the use statement be at the top if we wanted this function to only accept an Eloquent collection of the company model?
You can implement your own class that extends Collection and then specify that you'd like to use that for collections of your model. I've never used the functionality, but something like this should work:
<?php
namespace App\Support;
use Illuminate\Database\Eloquent\Collection;
class CompaniesCollection extends Collection {}
And then define the newCollection() method in your model:
<?php
namespace App\Models;
use App\Support\CompaniesCollection;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
public function newCollection(array $models = []): CompaniesCollection
{
return new CompaniesCollection($models);
}
}
And now your controller method can have a signature like this:
public function transformCompanies(App\Support\CompaniesCollection $companies);
Every time I dispatch a job the values passed in ProcessJob::dispatch($model_object) are not available in the handle() method of the ProcessJob class
I tried passing just the id of model record but it's still not available in the handle() of the ProcessJob Class
public function __construct(int $id)
{
$this->id = $id;
$this->links = collect([]);
$this->phone_numbers = collect([]);
$this->emails = collect([]);
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$columns = [];
logger($this->id);
}
I was expecting the logger($this->id) to log the id number but it doesn't.
id comes from
ProcessJob::dispatch($crawler_job->id)->onQueue('crawler');
I think you might be figuring out how it works by now. Just a quick tip:
You may not need to pass the model id into the job. Just pass the whole model object and use the SerializesModels trait.
use Illuminate\Queue\SerializesModels;
ProcessJob implements ShouldQueue
{
use SerializesModels;
public $model;
function __construct(TestModel $model)
{
$this->model = $model;
}
}
So when the job is serialised, the model id will be stored in the payload instead of the the whole object.
I'm trying to push all the model changes to the frontend using updated event. I don't want to send the whole model so I found the hasChanges() method. But it's always empty.
My first thought was that this event fires BEFORE the actual save but getDirty() is also empty. Then I thought in debug bar that for some reason it's retrieving the model once again (selecting from DB) right after updating it. Is this normal behaviour or is it just creating a new model object and not passing the existing one to the event?
Event:
class IcUpdated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
private $ic;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($ic)
{
$this->ic = $ic;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return [
new Channel('dashboard_' . ConfigHelper::getSelectedOrganizationId())
];
}
public function broadcastAs()
{
return 'ic.updated';
}
public function broadcastWith()
{
return $this->ic->getChanges();
}
}
Model:
protected $dispatchesEvents = [
'updated' => \App\Events\IcUpdated::class,
];
So how would I access and send only changed fields in event?
This is caused by the SerializesModels trait. This causes the model to be serialized to its primary key and then refetched from the database when the job is executed.
This is useful for cases where there is a delay on a queued job, for instance, you queue an email to go out to $user. The user changes their email address, the queued job runs but goes out to the new email address since it refetches the user from the database.
In your case, you definitely don't want to serialize the model to its key since you need properties stored in that specific model instance.
When a record gets deleted from my_items_table I want to insert the record into my_items_table_archive.
I could do this on each Controller, but would prefer to hook into the Eloquent model.
Is there anything like this?
Pseudocode:
class MyItem extends Model {
protected function beforeDelete($record) {
MyItemArchive::create($record); // add record the archive
return true; // continue deletion of $record
}
}
Any idea? Thanks!
Yes, there is something similar to your pseudocode.
You can utilise Eloquent Events
A good example of this can be seen below:
protected $dispatchesEvents = [
'deleted' => UserDeleted::class,
'deleting' => UserDeleting::class
];
The class in question just needs to adhere to / Follow: Listeners
You can also use Eloquent Observers / the observer pattern to achieve a similar result.
Let me know how you get on!
First of all create a new Observer using
php artisan make:observer MyItemObserver
Then
<?php
namespace App\Observers;
class MyItemObserver
{
public function deleting(MyItem $myItem)
{
/// insert new record here
}
}
Now you in your appServiceProvider
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
MyItem::observe(MyItemObserver::class);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
}
Now your obverserver will be hooked to Model Events.
Hope this helps.
As described in the official documentation you have two choices using the events. The first one is creating an observer like this:
class MyModelObserver
{
/**
* Listen to the Model deleting event.
*
* #param User $user
* #return void
*/
public function deleting(User $user)
{
// HERE YOUR CODE TO TRANSFER THE MODEL
}
}
Than you have to register it on your AppServiceProvider
public function boot {
MyModel::observe(MyModelObserver::class)
}
Otherwise you can add these events in your model by generating the specific class:
protected $dispatchesEvents = [
'deleting' => MyModelDeletingEvent::class,
];
Anyway if you're using a version of laravel lower than 5.4 you should check the documentation for the specific implementation, since the $dispatchesEvents is not available as variable.
I'm testing one of my controllers and no matter what I try I get the error that the all() function doesn't exist.
Static method Mockery_1_App_Models_User::all() does not exist on this mock object
My test method:
/**
* Test index page
* #return void
*/
public function testIndexAsUser()
{
$this->beUser();
// The method calls the mock objects should receive
$this->user->shouldReceive('all')->once()->andReturn([]);
// Call index page
$response = $this->call('GET', 'users');
// Assertions
$this->assertResponseOk();
$this->assertViewHas('user');
$this->assertViewNameIs('users.show');
}
My mocking method:
/**
* Mock a class
* #param string $class
* #return Mockery
*/
public function mock($class)
{
$mock = Mockery::mock('Eloquent', $class);
app()->instance($class, $mock);
return $mock;
}
My actual controller method:
/**
* Show all users
* #return Response
*/
public function getIndex()
{
$users = $this->user->all();
return view('users.index');
}
Am I using the wrong Eloquent class in my mock object or something? Since Laravel 5 the models are not referring to Eloquent but to Illuminate\Database\Eloquent\Model but I've tried that too.
The easiest way to mock an Eloquent model is by using partials:
$mock = m::mock('MyModelClass')->makePartial();
However, it won't help you much as you're using a static method (all()). PHP's not-so-strict nature lets you call static methods in a non-static way ($user->all()), but you should avoid it. Instead you should do it the heavy-handed way:
$users = $this->user->newQuery()->get();
This can be mocked:
$mockUser->shouldReceive('newQuery->get')->andReturn([/*...*/]);
If you want to take this one step further, move the get() call into a separate repository class that is injected into the controller, which will make it easier to mock. You can find plenty of articles on the repository pattern online.