I need an Observer called when an Attribute Set is Saved & Deleted. I can't seem to find the right event name for it.
After some digging, I figured out that the Model I need to tap into is http://docs.magentocommerce.com/Mage_Eav/Mage_Eav_Model_Entity_Attribute_Set.html
Since it implements the Mage_Core_Model_Abstract class, it should allow me access to the save_after, delete_after event names. But I can't seem to figure out the syntax for defining the observer for it.
I tried eav_attribute_set_save_after, model_eav_attribute_set_save_after, attribute_set_save_after but they don't seem to work.
What's the right event name?
If you had dug just a little bit more, you would have seen that these "generic" model events are formed like this :
Mage::dispatchEvent($this->_eventPrefix.'_save_after', $this->_getEventData());
Now if you look at the Mage_Eav_Model_Entity_Attribute_Set class you see the eventPrefix attribute defined as :
protected $_eventPrefix = 'eav_entity_attribute_set';
So your event will be :
eav_entity_attribute_set_save_after
Note that this protected attribute needs to be defined for each Model you want to access generic events (some magento models doesn't have this attribute setted by default, just be aware of it... you might have to add it yourself someday)
Related
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
I have made some changes to Mage_Core_Controller_Request_Http but in the file distributed by with magento. This is not the best way, i know, but i have not been able to work out how to override a file in the Controller directory. I can find out how to override files in the controllers directory.
Can anyone tell me how i can override Mage_Core_Controller_Request_Http in my own extension.
thanks
If you don't want to revert to the include path hack, you can also use reflection to set your own request class on the Mage_Core_Model_App model. You can use an observer for controller_front_init_before event for that.
I'll assume you are familiar how to create an event observer, soI'll only add the code for the observer method. If you need additional information please ask.
// Observer method
public function controllerFrontInitBefore(Varien_Event_Observer $observer)
{
$app = Mage::app();
$reflection = new ReflectionClass($app);
$property = $reflection->getProperty('_request');
$property->setAccessible(true);
$myRequest = new Your_Module_Controller_Request_Http();
$myRequest->setOrigRequest($app->getRequest()); // if needed
$property->setValue($app, $myRequest);
// Proof of concept:
// Loggs Your_Module_Controller_Request_Http
Mage::log(get_class(Mage::app()->getRequest()));
}
Create the class Your_Module_Controller_Request_Http and extend the original Mage_Core_Controller_Request_Http.
After that event your request object will be used instead of the original.
This enables you to stay as upgrade safe as possible, because you do not have to copy over the full class from the cor code pool.
Edit: Vinai's solution is the better one.
Because this class is instantiated directly, you will have to use the so-called include path hack to override.
The order of precedence for include paths which affect Varien_Autoload's work is set in app/Mage.php. That order is as follows:
app/code/local/
app/code/community/
app/code/core/
lib/
Therefore, if you copy your file to an analogous path under the local or community codepools, your definition of that class will be used.
Since Magento 1.7 you can use the method Mage::app()->setRequest($request) to replace the request object within an observer for the controller_front_init_before event as suggested by Vinai.
WARNING for Magento Enterprise: The Full Page Cache won't work with this method, as it relies on changes to the request object done before controller_front_init_before. You either need to manually copy all properties from the old request to the new - or replace the request class with benmarks solution.
I'm trying to extend the core to change how magento sets the status on products. Essentially, when an admin user tries to change the status of a product to disabled, I want it to check to see if the product is in stock in their EPOS system, and if it is, throw an error.
To do this, I was going to extend the model where the product status is set and rewrite that function. The problem is, I can't find this anywhere. There is nothing in magento_core_model_product. I found a function in mage_catalog_model_product_status called updateProductStatus, but this doesn't seem right either.
Does anybody know where I need to be looking to find this function?
After a bit of research, I found that Magento generates all the getters and setters pragmatically, through extensive use of the __call() function, which is called when a function that isn't defined is called.
To modify functionality of a getter or setter, simply define the function you want to modify in your rewrite of the class, and this will be called before the __call(), essentially rewriting the default functionality, in a roundabout way.
I am looking to create some new functionality to Magento. I am going to be looking to the url and grabbing a parameter. The issue is, this can be on any page. So I can't just extend a Catalog or Checkout Module.
I thought about extending the session classes but I wasn't sure it really fit. Basically I want to grab a parameter from the url and from there add some functionality if it is set or not. I don't think a class will get auto loaded unless it is instantiated somewhere else with a getModel method, am I wrong?
How can you add a module that doesn't have a url path for controller and what not, but doesn't fit extending one of the core modules?
I looked for a generic event but didn't really see one like before_page_load or something
Take a look at the event controller_action_predispatch in app/code/core/Mage/Core/Controller/Varien/Action.php. This event should get called on each dispatch and allow you to grab whatever parameters you need.
The event passes the controller as data, so you can do this:
function yourEvent( $event ) {
$controller = $event->getController();
// your processing here
}
Let me know if that doesn't fit the bill. Hope that helps!
Thanks,
Joe
I am dealing with Magento for a while and i find it very interesting and probably my future choice of work tool. Though i have some troubles understanding some of the stuff going on. If i call www.store.com/catalog/product/view/id/2 then the product controller executes from the catalog core module, in it the product is being fetched via _initProduct() method first in which this event is being dispatched:
Mage::dispatchEvent('catalog_controller_product_init_before', array('controller_action'=>$this));.
Which class/method is being called? As i understood that should be a method from observer class which is under Model folder and it should be defined in the etc/config.xml file.
Some of the events defined in the config.xml are executed automatically... (why?) where is defined the one used in the viewAction() from the ProductController.php in Catalog module? How i can send and use array data to the observer's methods, cause i saw some of them contains this method: Mage::app()->reinitStores() which re-inits store, group and website collections and it's not something simple. I find this very powerful and i really want to know the posibilities with using Observers and Events.
Event observers can be defined in the config.xml for any module that is active in the system, they don't necessarily have to be defined in the same module.
You can send data to the event observers by adding information to the event object, which is done in an array defined as the second argument to dispatchEvent. Just add more elements to the array and the event observer method can extract it from $observer->getEvent(). You are also free to define your own events by calling the same dispatchEvent method.
One of the handy things about most Magento models is that they are inherited from the Mage_Core_Model_Abstract class which includes event for _load_after, _save_before, _save_after, _delete_before and _delete_after. For example, the product model has catalog_product_load_after, catalog_product_save_before, etc.
Hope that gives you some more information about the possibilities of using events.