Laravel observer pattern for keeping two related models in sync - laravel

I'm using Laravel 5.4. I have two related eloquent models:
ImageFile and ClipartImage.
ClipartImage contains a belongsTo relationship as follows:
class ClipartImage extends Model
{
public function image_file()
{
return $this->belongsTo(ImageFile::class, 'image_file_id');
}
public function promote()
{
$this->image_file->promote();
}
}
On ClipartImage there is a "promote" method which will pass the promote call through to the ImageFile object as it knows how to promote itself.
The problem I'm running into is if properties change on ImageFile as a result of the method call, there are a couple of properties on the related ClipartImage which may need to be updated as a result. In the ImageFile class I could search the database for any related ClipartImages and update the relevant fields, but this doesn't update the existing ClipartImage instance that I am working with. What's the normal way to solve this sort of dependency between models and keeping the object instances in memory in sync?
I guess I'm in need of some sort of Observer pattern but in Laravel these things seem to be handled at the static/class level not for individual object instances - ie I could create an observer class that looked for any changes to ImageFile models, but that isn't going to help me refresh or update my existing ClipartImage object.
Edit: I've used a single "promote" method to illustrate, but there's a bunch of interaction between ClipartImage and the ImageFile class and it's not obvious from the point of view of ClipartImage when it may need to refresh or update itself. Ideally ImageFile would inform it when key fields have changed so it can respond accordingly.

Related

MorphToMany relation empty in `created` Model Observer

I am using Laravel Nova and have an Entry model that uses spatie/nova-tags-field. (Under the hood this package uses a morphToMany relation between models and tags via a trait)
I attached a model observer on the created event.
I want to access the entry's tags in the observer like this:
public function created(Entry $entry)
{
$tags = $entry->tags;
}
But $tags is always an empty array [], yet I can access the models tags later (not in the observer, but anywhere else) using Entry::find($id)->tags. My guess is that the morphToMany pivot table entry for the attached tags is being created after the observer fires?
Thank for your input.
Because the model hasn't been created yet, try the created method in your observer instead of creating
You could override the attachTags method in your Entry and fire a custom event when $this->wasRecentlyCreated is true.
Also you could listen to the created event (or any other) of your own tag model.
In combination with $touches something close to your desired behaviour may be possible. But obviously the created event of your Entry will always be fired before any tags are attached.

What's the most reliable way to catch saves and deletes in Laravel 5.2?

I need to run some code when one of my models is saved (created/updated) or deleted. What's the best way to do that?
There's three different ways that I'm aware of:
Override the save and delete methods on the model
Add creating/updating/deleting callbacks in the boot method
Bind an observer in the boot method
I haven't seen these compared and contrasted, so I don't know what the differences are. I'm worried that the events won't fire under certain conditions.
For example, in Django, deletes only fire if you delete the models one-by-one, but not in a mass delete.
To be clear, I'm looking for answers that compare and contrast these (or other) methods -- not simply suggest even more ways of doing the same thing.
It's just my opinion for several methods you mention previously.
Override the save and delete methods on the model ( If you override it then next update of Laravel change visibility of method your code does not work again. It would throw Exception or PHP error. You have to modify it to work again )
Add creating/updating/deleting callbacks in the boot method ( exist in Laravel 4 you should check it again in Laravel 5 maybe different implementation using Event and Listener )
Bind an observer in the boot method ( exist in Laravel 4 you should check it again in Laravel 5 maybe different implementation using Event and Listener )
I think you should using Event and Listener provided by Laravel. It maybe still work on next Laravel Update. I assume Event and Listener as minor change area in Laravel and changed maybe just different method implementation.
Laravel should have plan of development assign which part of Laravel will be developed as major change area ( big modification ) or minor change area ( little modification ). If you try to change or override major change area it would can't be used on next Laravel Update.
You can register Event and Listener for save and delete record. Laravel have fireModelEvent method on Model ( Illuminate\Database\Eloquent\Model ) which trigger specific Laravel Event. If you've registered Event, Dispatcher ( Illuminate\Events\Dispatcher ) will execute Listener of Event.
Documentation about Laravel Events:
https://laravel.com/docs/5.3/events
https://laravel.com/docs/5.2/events
I assume you have YourModel as Model then do the following action on the below.
Register Event and Listener. Open app\Providers\EventServiceProvider.php then Add Event and Listener to EventServiceProvider.listen properties for YourModel or follow Laravel Documentation to create event using other way.
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
...
'eloquent.saved: App\YourModel' => [
'App\YourModel#eventSaved',
],
];
}
Adding eventSaved method on App\YourModel as Listener for Event so you can do specific action after save or delete.
class YourModel extends Model
{
public function eventSaved(){
// You can add your code to catch save here
}
}
The three methods and 4th referred by #joko. There may be more as well but lets focus on the 4 methods.
Let me describe you them one by one:
1) Override the save and delete methods on the model
In this method you are using OOPD method overriding. You are overriding Laravel's interal save method and adding your additional code by defining your own save method on top of it. This should be avoided as Laravel keep evolving and it may happen that thing start to fail if major change is done like Suppose in future laravel replace save method with any other method to save the records. Then again you will have to create another method to override that new method. Also writing code here may grow your model class file. You model may keep handling things like he shouldn't handle(Example: Sending Email). This method should be avoided.
2) Add creating/updating/deleting callbacks in the boot method
Here you are defining code on the Boot method of the Model. This method should only be used if there is much little code/things that you need to handle on event. The drawback of this method is that it make code more complicated and cluttered as you may write all logic in one like like functional programming. Suppose if you have to do some stuff on before creating and after created. You boot method will grow.
3) Bind an observer in the boot method
This method is pretty good. You create one observer class which handles such stuff that what should happen on Laravel events. It makes code more cleaner and easy to maintain.
Example: Suppose you have to write code in creating, saving, saved, deleting in these methods. In this case, method 1) and method 2) won't be good practice because in
Method 1: We will have to create this 4 methods and override them as well and support them in future releases of Laravel. In this case, code in your Model will also grow because of overriding this methods
Method 2: In this case your boot method will grow as well so you Model file will become a junk of code.
In method 1 and 2 also remember that its not responsibility of your Model to do many of the stuff that you going to write. Like sending email when user is created. These codes you may end up writing in created method.
Suppose now you have scenario where you need to send email to user on created event as well as you need to make user's entry log user in customer CRM. then you will have to write code for both in same method. Probably, you may not following single responsibility principle there. What should we do in the case? See method 4.
4) Other method suggested by #joko
The scenario that i suggested in method 4's end. You may send email to user and log him in Customer CRM whenever it is created. Then your method will do 2 things(Sending email and logging in CRM). It may not following single responsibility principle. What if better we can decouple both of them. Then comes this method.
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'eloquent.saved: App\User' => 'App\Listeners\SendWelcomeEmailToUser'
'eloquent.saved: App\User' => 'App\Listeners\LogUserInCRM'
];
}
Create two listener classes:
class SendWelcomeEmailToUser
{
public function handle(User $user){
// Write code to send email
}
}
class LogUserInCRM
{
public function handle(User $user){
// Write code to log
}
}
Through this you can separate out codes and make them more cleaner.
I generally prefer this method its mode clean. It also gives you much better idea that what actually happen when event happens. It becomes one single point for Event to Listener mapping.
You can create event handlers, for every create/update of model, for example to add to cache the model data which is just saved to database or going to save to database, easier to retrieve without select query call,
while delete call, use forget for given key on cache handler event to delete cache as well as to delete from database too.
I'm partial to doing things manually when you need to know exactly how they're done. I recently used this Laravel Boilerplate to start a project and I like the way they manually fire events in the repository when a model is updated:
https://github.com/rappasoft/laravel-5-boilerplate/blob/master/app/Repositories/Backend/Access/User/EloquentUserRepository.php
Since models should always be updated through the repository, you always get to manually decide how events are handled. You could fire your own event when multiple models are deleted, and act accordingly. All of your options will work though, you just need to find the option that suits your needs best.
You can create abstract Model class that extends Illuminate\Database\Eloquent\Model class and all your model will extend this class. With implementation like this you can have more control on the models. For example
<?php
namespace App\Base\Database;
use Illuminate\Database\Eloquent\Model as BaseModel;
abstract class Model extends BaseModel
{
public function save(array $options = [])
{
//your code here
return parent::save($options);
}
}
You can do this for all the methods of the Model class and also you can add additional methods that are relevant for all models in your application

Entity Framework in detached mode with MVC application

I have started working out with Entity Framework (EF) for an MVC n-tier application. It would seem that very obvious that this being a web application (which is stateless), I would have to use detached object models. There is no ambiguity with doing an Add operation. However when doing an edit there are here are two ways
Fetch the original object in context, attach the updated object and
then save to database. Something like mentioned in answer to this
question
EF4 Context.ApplyCurrentValues does not update current values
Set individual modified properties explicitly using the IsModified property of individual fields of the object like
mentioned in this article
http://msdn.microsoft.com/en-us/data/jj592677.aspx
Method 1 has disadvantage of having to load object into memory from database each time an update needs to be performed.
Method 2 would require having to manually pass which fields to be set as IsModified to true from wherever the object an be updated. So for e.g. for each object, I may need to create a boolean collection object for each field of the object.
e.g.
SaveEntity(EntityClass e, EntityStateClass ec)
{
context.Entry(e).Property("Name").IsModified = ec.NameState;
context.SaveChanges();
}
class EntityStateClass{ public bool NameState;}
I would prefer method 2 simply for the sake of performance but I am hindered by the n-tier architecture and repository pattern I am using. My Repository interface restricts save method for any object to be
SaveEntity(EntityClass e);
So I cannot pass the "state" object. Context class is not available and should not be available outside DAL. So I cannot set property outside. Is there any "proper" way to achieve this ?
Note: Self-Tracking Entity is also out of question since I cannot send entities with state to client (the browser) since I am intent on keeping the html lightweight.
EDIT: After a lot of thinking, I am trying to use following mechanism to keep track of modified state for each field in my domain class
Declare a partial class for entity class.
For each field that is updateable, declare a boolean property like "IsModified_FieldName"
Set the "IsModified_FieldName" property when the field is set.
However for this I need Entity Framework to generate explicit properties for me instead of implicit properties that it auto-generates. Does EF provide an handle to do this ?
Here is sample code of what I am trying to achieve
//Save Method for class EntityClass.
SaveEntity(EntityClass e)
{
context.Entry(e).Property("Name").IsModified = e.IsModified_Name;
context.SaveChanges();
}
//EntityClass is class autogenerated by EF
public partial class EntityClass
{
//This is auto-generated property by EF
public string Name {get; set;}
/* This is what I would like EF to do
private string name;
public string Name
{
get {return Name;}
set {
name = value;
//this is what I would like to do
this.IsModified_Name = true;
};
}
*/
}
//This is another partial definition for EntityClass that I will provide
public partial class EntityClass
{
//This property will be set to true if "Name" is set
public bool IsModified_Name {get; set;}
}
PS: It seems the information I have provided is not sufficient and therefore there are no responses.
I am using DbContext (Database first model)
EF auto-generates the class files for me. So each time I update my database, the class files are regenerated.
To your concrete question: The entities are generated by a T4 template and it should be possible to modify this template (which is in text format) to generate the entities in a way you want to shape them.
But I have a few remarks about your concept:
In a web application data are usually changed by a user in a browser. To have a definite knowledge what really has been changed you need to track the changes in the browser (probably by some Javascript that sets flags in the data (a ViewModel for example) when a user edits a text box for instance).
If you don't track the changes in the browser what happens? The data get posted back to the server and you don't know at the server side (with MVC in a controller) which property has been changed. So, your only chance is to map all properties that has been posted back to your EntityClass and every property will be marked as Modified, no matter if the user really did a change or not. When you later call SaveChanges EF will write an UPDATE statement that involves all those properties and you have an unnecessary overhead that you you want to avoid.
So, what did you win by setting individual properties instead of setting the whole entity's state to Modified? In both cases you have marked all properties as Modified. Exceptions are partial changes of an entity, for example: You have a Customer entity that has a Name and City property and a view that only allows to edit the Name but not the City and a corresponding ViewModel that only contains a Name property. In this case your procedure would only mark the Name property of the Customer entity as Modified but not the City. You might save here a little bit because you don't save the City property value to the database. But you still save the Name even if it didn't change.
If you use solution 1 (ApplyCurrentValues) you have to load the entity first from the database, yes, but it would only mark the properties as Modified that really changed compared to their values in the database. If the user didn't change anything no UPDATE would be written at all.
Keep in mind that you are only at the beginning to implement your concept. There are other changes to the data that can happen in the browser than only scalar property changes, namely relationship changes. For example a user changes the relationship from an Order to a Customer or you have a view that has an Order and a collection of OrderItems and the user cannot only edit the Order header but also edit the OrderItems and remove and add new OrderItems. How do you want to recognize when the data come back from the browser to the server which collection item has been added and which has been removed - unless you track all those changes in the browser and send tracking information back to the server in addition to the actual data or unless you reload the Order and OrderItems from the database and merge the changes into the original entities from the database?
Personally I would vote for option 1 for these reasons:
You can use real POCOs that don't carry additional tracking information. (BTW: I have some doubt if you aren't reinventing the wheel by implementing your own tracking that EF change tracking proxies provide out of the box.)
You don't need to track changes in the browser which can become quite complex and will require Javascript in every Edit view to write change flags into hidden form fields or something.
You can use standard features of EF without having to implement your own tracking.
You are required to load entities from the database when you want to update an entity, that's true. But is this the real performance bottleneck in a web application where data have to run through the wire back and forth (and reflection (which isn't really known as to be fast) is involved by the model binder)? I have nothing said if your database is remote from the web server and connected by a 9600 baud modem. But otherwise, your plan is not only premature optimization, it is kind of premature architecture. You are starting to build a potentially complex architecture based on "it could be slow" to solve a performance problem that you actually don't know of whether it really exists.

Bind customizable views to dynamic models

We are working on an ASP.NET MVC 3 using ext.net and EF 4.
Data model is mapped using EF4.
Views’ content is rendered from customizable XML files.
Example: Within one view, I can display fields that are related to both objects “customer” and “order”, so from this view I can modify the customer data and also add a new order.
How can we bind the view to the custom model that contains 2 objects (customer and order)? Using non strongly typed views will require a source code that will check all different possibilities (If I remove/add a field to display from the XML file, object constructor and CRUD operations parameters will change also.
We are wondering how can we handle such dynamic application?
Is this a common issue that was raised before? Or is there any solution to use dynamic views bound to custom model (object, xml, etc.)?
Your help is very appreciated, please enlighten me.
Based on what you replied to my comment, I can defenitely say that you need strongly typed views. That said, you decide what the model of your view is. If your view needs to manage users and orders at the same time, you can make a class like this:
public class MyCustomViewData
{
public IEnumerable<User> Users {get;set;}
public IEnumerable<Order> Orders {get;set;}
}
and then strongly type your view to MyCustomViewData and you're set. My example is oversimplified but I think you can get the point.
Unless I'm missing something, I believe the normal way round this would be to strongly type your view to say 'user' and then on the user object, define a property which is a collection of 'orders'.

MVC - helpers and Joomla

I'm being confused, I have a function that needs to update some table, I have placed it inside controller, however now I have found that I will need to use it inside other 2 controllers.
What is the best practice to place the function that making updates and where to place it and how to call it?
maybe helper?
Did you create your models by extending JTable? In this case, just add the method there. It's perfectly ok to have business logic in the model such as "increment all rows of this user id by one" (static method) or "split up this name and save it into columns first name & second name" (normal method).
The helper would need to be relevant to the controller, as it will be applied to all controllers. I will assume that not all controllers would see meaning in calling the Update.
You could create a static class with a static method that would update your table.
etc Add a file to the Models folder and then create the following class.
public static CalledFromMultipleLocations
{
public static void UpdateMyTable(string somedata)
{
//Do you update code.
}
}
Without knowing your business object model its hard to really provide a solution.

Resources