Yii: attaching events to models - yii

I have a User model which is bundled in a module installed on my Yii application. This module is third party and I do not want to alter its code.
I also have a Cv Model that has a BELONGS_TO relation with the User model.
My question is: How can I delete the cv when a user is deleted ?
I know that I can achieve this with on delete cascade ... on mysql. However, i do need to delete other data such as a photo, files, etc.
What I have tried
I have created a component that is preloaded on my application. This component attaches to an onAfterDelete event
class EventListener extends CComponent
{
public function init() {
Yii::import("application.modules.users.models.User");
User::model()->attachEventHandler('onAfterDelete', array($this, 'deleteUser'));
}
public function deleteUser($event)
{
// stuff here ...
}
}
However this does not work.
Any suggestions ?

This may help you.
User::model() is a singleton
$user1 = User::model();
$user2 = new User; // will be used in insert action
$user3 = User::model()->findByPk(10); // will be used in update/delete action
$user1, $user2 and $user3 are completely different objects.
You can attach events to objects, in this case you have to add events to all these 3 objects individually.
$user1->attachEventHandler(...);
$user2->attachEventHandler(...);
$user3->attachEventHandler(...);
look like Yii does not provide any way to add events at Class level.

Well, guys, I have just stumbled upon the same problem and I solved it this way:
You should use the init() of a Model, not your event listener collection class.
In my case I have devModel class:
public function init()
{
parent::init();
$this->onLicenseUpdated = array(new MEventProcessor, 'licenseUpdateHandler');
}
And the handler is licenseUpdateHandler($event) in a MEventProcessor class.
This way every time you work with model instance, it'll call init() for every object and attach the event handler to every instance of this Model.
Now any time the event (in my case onLicenseUpdated()) is invoked for the model - the handler will be called too.

You could also to use Behaviors.
1 - behaviors can listen to events : you just have to override their events() method
class MyBehavior extends Behavior {
public function events() {
return [
ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
];
}
public function beforeValidate($event) {
// ...
}
}
2 - although it is more common to attach a behavior to a component in the component's behaviors() method, you can also attach them dynamically and keep the original code unmodified :
use app\components\MyBehavior;
// attach a behavior object
$component->attachBehavior('myBehavior1', new MyBehavior);
You will find some useful documentation here :
yii 1 : http://www.yiiframework.com/doc/api/1.1/CBehavior
yii 2 : http://www.yiiframework.com/doc-2.0/guide-concept-behaviors.html

Related

Access to container of Simple Injector MVC views

In a Sitecore project I've integrated Simple Injector using this article
It uses sitecore pipelines and then uses a method in App_start
namespace BBC.App_Start
{
public class SimpleInjector : IPackage
{
public void RegisterServices(Container container)
{
GetContainer.RegisterServices(container);
container.Register(() => new SitecoreContext(), Lifestyle.Scoped);
container.Register(() => new Container(), Lifestyle.Singleton);
}
}
}
Simply I can inject container into controller constructor but can't have container in View files.
I tried to declare a static property in App-start and save container to it. but still I'm getting no registration type in Views
What is the best way to have container object in views?
As Stephen suggests in his comment, the literal answer to your question is "you shouldn't do that - because it's not really the way MVC and DI are supposed to work". The more detailed answer goes something like this:
The job of your view is to present data that it has been passed via the Model. Views should not really contain logic. Very simple stuff like "if flag is false, hide this block of mark-up" is ok, but the more complex code to work out what the value of the flag is shouldn't be in the view.
MVC tries to make our website code better by encouraging you to separate presentation (the View) from data (the Model) and logic (the Controller). This should make our code easier to work with - So if you have processing that needs doing, then it should really be happening when your controller method runs.
If your view requires some special data, best practice suggests it should work it out in the controller method and pass it to the view in the model. The code might look more like this:
public class MyModel
{
public string SpecialData { get; set; }
}
public class MyController : Controller
{
public ActionResult DoSomething()
{
// do whatever processing is needed
var somethingCalculate = resultFromYourOtherObject();
// do other stuff
var model = new MyModel() { SpecialData = somethingCalculated };
return View(model);
}
}
And then the View just needs to accept the MyModel class as its model, and render the SpecialData property - no logic required.
I think also it's considered a bad idea to have calls to fetch objects from your DI container spread about your codebase. For MVC apps, generally your DI container gets wired in to the process of creating a controller for a request when the app starts up. Rather than passing about a DI Container into your controllers, the DI framework extends the Controller-creation process, and the container isn't exposed outside of this. When the MVC runtime needs to create a controller, the controller-creation logic uses the DI framework to fetch objects for all the controller's dependencies.
Without more detail about what you actually want to achieve, it's difficult to say what the "right" approach to creating your object(s) here is, but the two most common patterns are probably:
1) Constructor injection: Your controller has a parameter which accepts the object required. The DI container creates this object for you at the point where it creates the controller, so your controller gets all its dependencies when it is created. Good for: scenarios where you know how to create the object at the beginning of the request.
public interface IMySpecialObject
{
string DoSomething();
}
public class MyController : Controller
{
private IMySpecialObject _specialObject;
public MyController(IMySpecialObject specialObject)
{
_specialObject = specialObject;
}
public ActionResult RenderAView()
{
// do some stuff
var data = _specialObject.DoSomething();
return View(data);
}
}
As long as IMySpecialObject and a concrete implementation for it are registered with your DI container when your app starts up, all is well.
2) Factory classes: Sometimes, however, the object in question might be optional, or it might require data that's not available at controller-creation time to create it. In that case, your DI framework could pass in a Factory object to your controller, and this is used to do the construction of the special object later.
public interface ISpecialFactory
{
ISpecialObject CreateSpecialObject(object data);
}
public class MyController : Controller
{
private IMySpecialFactory _specialFactory;
public MyController(IMySpecialFactory specialFactory)
{
_specialFactory = specialFactory;
}
public ActionResult RenderAView()
{
// do some stuff
if( requireSpecialObject )
{
var data = getSomeData();
var specialObject = _specialFactory.CreateSpecialObject(data);
var data = _specialObject.DoSomething();
return View(data);
}
return View("someOtherView");
}
}
But a good book on using DI may suggest other approaches that fit your specific problem better.

Connecting data to a GUI - OOP

I have an application with several graphs and tables on it.
I worked fast and just made classes like Graph and Table that each contained a request object (pseudo-code):
class Graph {
private request;
public function setDateRange(dateRange) {
request.setDateRange(dateRange);
}
public function refresh() {
request.getData(function() {
//refresh the display
});
}
}
Upon a GUI event (say, someone changes the date range dropdown), I'd just call the setters on the Graph instance and then refresh it. Well, when I added other GUI elements like tables and whatnot, they all basically had similar methods (setDateRange and other things common to the request).
What are some more elegant OOP ways of doing this?
The application is very simple and I don't want to over-architect it, but I also don't want to have a bunch of classes with basically the same methods that are just routing to a request object. I also don't want to set up each GUI class as inheriting from the request class, but I'm open to any ideas really.
As you commented the methods are identical. In that case I would suggest the following approach.
abstract class AbstractGUIElement {
protected request;
public function setDateRange(dateRange) {
request.setDateRange(dateRange);
}
abstract function refresh();
}
Refreshing would probably be element specific so I have added it as an abstract method the inheriting types have to implement.
class Graph extends AbstractGUIElement {
public function refresh() {
// Graph specific refreshing
}
}

Zend_Form disable populate before isValid()

Im not able to disable populating values in Zend_Form .
I have my own form class. With hidden token element where I would like to dynamicaly setup random value every time the form is called (or reposted). I thought that setValue will make the job.
class MY_Form_Test extends Zend_Form {
public function init() {
...
$this->addElement('hidden', 'token');
$this->getElement('token')->setValue(uniqid('',true));
...
}
BUT: When I have simple controller like this. Zend automate populating old hidden values except to generate new one.
$form = new JC_Form_Test();
if($form->isValid($_POST)){
// Action ...
}
else{
// Error
}
SOLUTION: The only solution I found is to call setValue in Controler AND AFTER isValid method. eg. in Error block.
QUESTION: Is there any way to setup element values directly in form class OR disable populate values in form class or before isValid() is called?
I think it's the best way to do it.
I work much with Zend Framework and have my own library for overwrite some Zend classes.
It's not bad to change something, but don't do it directly within Zend Framework
SECOND SOLUTION: Second solution I found is to overload isValid() method in Form class. Like this. Then I dont need to put setValue() into every Controller.
class MY_Form_Test extends Zend_Form {
...
public function isValid($data){
// Propagate values
$valid = parent::isValid($data);
$this->getElement('token')->setValue(uniqid('',true));
return $valid;
}
Are there any other solution eg. some element option to do this job more simple?

EventAggregation quick start?

I am created a MainView that it's DataContext is a MainViewModel initialized in xaml.
The MainView contains a ContentControl that is bound to the Content property of the MainViewModel.
I added some content in the MainViewModel constructor, so that if the current user is not logged in, it automatucally loads LoginView (and correspondingly it's DataContext LoginViewModel) into this Content property.
Now my question is, what should I do when the user successfully logs in:
'To be called from the LoginCommand
Private Sub Login
'Do Login
If WebContext.Current.User.IsAuthenticated Then
' - Publish a global event to be subscribed and caught from the MainViewModel
' - Close LoginView
' - The MainViewModel should set it's Content property back
' to what the user initially intended to open
End If
End Sub
How is this done?
Note: I prefer using prism's EventAggregator rathen then other stuff, but I have no clue:
How to spread it out between the ViewModels
How to create events (I don't need to pass parameter, nor do I need it to be generic, just Action, LoginAction - no parameters.
How do I subscribe from the MainViewMode.
I do NOT use MEF or Unity, nor do I use seperated modules, all my application is in one single assembly.
I prefer not to write any code in the code-behind at all
Answer in both VB.NET or C# are welcommed the same
Any help would be recommended
You can go here for info regarding the EventAggregator.
You could also use the following code to create an instance of the EventAggregator without using MEF or Unity:
internal static class EventAggregatorHelper
{
private static IEventAggregator _Current = new EventAggregator();
public static IEventAggregator Current
{
get
{
return _Current;
}
}
}
And you could then call the EventAggregator like so passing in the required information to the aggregator:
EventAggregatorHelper.Current.GetEvent<SelectedItemChangedEvent>().
Subscribe(HandleSelectedItemChangedEvent);
In this case the SelectedItemChangedEvent and the subscriber that deals with this event.
The SelectedItemChangedEvent is a class declared as shown below:
public class SelectedItemChangedEvent : CompositePresentationEvent<String>
{
}
and the subscriber would be something like this:
internal void HandleSelectedItemChangedEvent(string viewName)
{
if (!String.IsNullOrEmpty(viewName))
{
//Do whatever you need to do here.
}
}
The link to the Event Aggregator I posted at the start should clear most things up for you.
Hope this helps.

robotlegs: I have 2 views and mediators that have in common 1 thing how can I DRY it?

I have 2 views that show lists that then uses a mediator to get data. but I want in some way to DRY it so I don't have to repeat my self 2 times for the same thing. how do I do it?
EDIT1(code):
[SkinPart(required="false")]
public var WOListsDDL:DropDownList;
// in partadded
case WOListsDDL:
// when the selected list is changed in the lists drop down list
WOListsDDL.addEventListener(IndexChangeEvent.CHANGE, _WOListsDDL_changeHandler);
WOListsDDL.dataProvider = new ArrayCollection();
WOListsDDL.labelField = 'title';
break;
//
protected function _WOListsDDL_changeHandler(event:*):void{
_debug('List selection changed handler.');
_getContentsForList();
}
protected function _getContentsForList():void{
_debug('Getting list items.');
getItemsSignal.dispatch({key: getSelectedList()._key, itemType: 'item'});
}
public var getItemsSignal:GetItemsSignal = new GetItemsSignal();
override public function mediatorComplete():void{
getItemsSignal.dispatch({key: tottysAuth.getCurrentUser()._key, itemType: 'list'});
}
// then in my mediator
[Inject] public var getItemsSignal:GetItemsSignal;
override public function onRegister():void{
// view listeners
view.getItemsSignal.add(_getItemsSignalHandler);
}
protected function _getItemsSignalHandler(input:Object):void{
getItemsSignal.dispatch(input);
}
this all for one view-mediator. now I have 2 view-mediators that are doing these tasks. How to make them dry?
Solutions I have:
make a little view containing the dropdown list with a mediator that is listening for the event. in the big components they are listening for a signal in the view of this little component. nothing more. it seems quite well but i don't think is so great
So there is functionality on both the views and the mediators that you'd like not to repeat?
You could put the code you want to reuse in another object and call its methods from your multiple views and mediators. Or you could put it in ancestor classes and extend each of those classes in both your view and your mediator.
Best practice would be to do the former and not the latter.