Please upgrade here. These earlier versions are no longer being updated and have security issues.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

Passing data between firing an event and handling the fired event

BeyBey
edited December 2011 in Vanilla 2.0 - 2.8

New to Vanilla, PHP, etc. and using it on Win7 localhost wamp server.

Is it possible to fire an event and include some data as part of the event context to be processed by the handler of the event?

For example ('hallucinatory code' in the plugin's default.php):

...

// When firing the event, include a string in the event firing context

$this->FireEvent('BeforeDiscussionRender', 'SomethingOrOther'));

...

// Handling the event before rendering ... act on the $EventContext

public function Base_Render_Before($Sender, $EventContext = '') {
if (isset($EventContext)) {// DoSomething} else {//DoSomethingElse};
}

Best Answers

  • TimTim Vanilla Staff
    edited December 2011 Answer ✓

    When you fire an event, the handler always gets passed a reference to the firing object ($Sender). So you can set data on that object before firing, and read it off the $Sender in the handler.

    $this->EventArguments['SomeKey'] = 'moo';
    $this->FireEvent('ExampleEvent');
    
    public function SomeObject_ExampleEvent_Handler($Sender) {
       if ($Sender->EventArguments['SomeKey'] == 'moo') echo "Cows say moo";
    }

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • sahotataransahotataran ✭✭✭
    edited December 2011 Answer ✓

    i think it should be

    DiscussionController_PopulateTitle_Handler($Sender) {

    or DiscussionsController_PopulateTitle_Handler($Sender) {

    rather than just Discussion_PopulateTitle_Handler($Sender) {

    because you specify the ControllerName for which you are firing the event for

    There was an error rendering this rich post.

  • TimTim Vanilla Staff
    Answer ✓

    @sahotataran is right. You specify the firing object's full class name as the first piece, then the event name itself, then Handler

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • TimTim Vanilla Staff
    Answer ✓

    Do yourself a favor.

    At the place where you're trying to fire that event, put this:

    var_dump(get_class($this)); die();

    That class name is what you're firing on, and should be the prefixed controller name in your handler hook. I'm willing to bet it is not DiscussionController.

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • sahotataransahotataran ✭✭✭
    Answer ✓

    basically if you check /applications/vanilla/views/discussion/index.php you will find

    <?php $this->FireEvent('BeforeDiscussion'); ?>

    just an example. you will find same definitions through out Vanilla where EVENTS ARE DEFINED and their PLACE where they are to be fired.

    still if you dont get it then you can download and install http://vanillaforums.org/addon/eventi-plugin and it will tell you all the events that are fired and where it is fired when the page is rendered.

    hope that helps

    There was an error rendering this rich post.

  • sahotataransahotataran ✭✭✭
    Answer ✓

    if you just wanna change the TITLE of page then you can just use

    Base_Render_Before($Sender){
    $Sender -> Head -> Title('Your Title');
    }

    There was an error rendering this rich post.

  • sahotataransahotataran ✭✭✭
    Answer ✓

    did you check the eventi plugin???? i will tell you all events that are fired.

    second how i would do is using any hook eg Base_Render_Before you can print_r($Sender)

    this will show in browser all the detail of the $Sender - then in your browser you can look for TITLE i mean search for title it will show where it is in which array/object etc and then using a hook i would try to change the title dynamically as you are trying to do

    There was an error rendering this rich post.

  • sahotataransahotataran ✭✭✭
    Answer ✓

    i think all routing is done by bootstrap.php

    There was an error rendering this rich post.

  • TimTim Vanilla Staff
    edited December 2011 Answer ✓

    Tim said:
    You specify the firing object's full class name as the first piece, then the event name itself, then Handler

    The reason your event handler before (ZoomitController_PopulateTitle_Handler) did not fire is because you arbitrarily added "Controller" to the name of your object. Read my quote. You specify the firing object's full class name as the first piece.

    That means if get_class($this) returns Hamburger, your event handler for events your fire off of that object will start with Hamburger_, not HamburgerController_ or any other word.

    So if, as you say, it returned Zoomit, then you would handle events thrown off of that object with a handler method starting with Zoomit_.
    In your particular case, if you're doing $this->FireEvent('PopulateTitle'), you'd hook that event with a method called Zoomit_PopulateTitle_Handler($Sender)

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • TimTim Vanilla Staff
    Answer ✓

    As far as accessing the URL and its parameters, we have an object called Gdn_Request which does that. It has a ton of helper methods.

    You can access this object with the helper method Gdn::Request(). In your case, to get the value of the passed-in URL parameter 'Type', you'd do something like:

    echo Gdn::Request()->GetValue('Type')

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • TimTim Vanilla Staff
    Answer ✓

    Gdn_Request has many other parameters and methods though, so to get an idea of everything it stores, do a quick var_dump(Gdn::Request()); die(); somewhere and have a look. Then inspect /library/core/class.request.php

    Vanilla Forums COO [GitHub, Twitter, About.me]

Answers

  • TimTim Vanilla Staff
    edited December 2011 Answer ✓

    When you fire an event, the handler always gets passed a reference to the firing object ($Sender). So you can set data on that object before firing, and read it off the $Sender in the handler.

    $this->EventArguments['SomeKey'] = 'moo';
    $this->FireEvent('ExampleEvent');
    
    public function SomeObject_ExampleEvent_Handler($Sender) {
       if ($Sender->EventArguments['SomeKey'] == 'moo') echo "Cows say moo";
    }

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • Thanks Tim!

  • BeyBey
    edited December 2011

    I tried that and the event doesn't get handled (or so it seems).

    In my case:

    ...

    $this->EventArguments['Title'] = 'Discussion Title Of Sorts';

    $this->FireEvent('PopulateTitle'); // Pre-populate discussion title

    $this->Redirect("post/discussion/" . $CategoryID); // Display new discussion

    ...

    public function Discussion_PopulateTitle_Handler($Sender) {

    die($Sender->EventArguments);

    }

    ...

    No funeral observed - New Discussion page is happily displayed.
    What am i doing wrong?

  • sahotataransahotataran ✭✭✭
    edited December 2011 Answer ✓

    i think it should be

    DiscussionController_PopulateTitle_Handler($Sender) {

    or DiscussionsController_PopulateTitle_Handler($Sender) {

    rather than just Discussion_PopulateTitle_Handler($Sender) {

    because you specify the ControllerName for which you are firing the event for

    There was an error rendering this rich post.

  • TimTim Vanilla Staff
    Answer ✓

    @sahotataran is right. You specify the firing object's full class name as the first piece, then the event name itself, then Handler

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • BeyBey
    edited December 2011

    Thanks.

    I tried both ways (and then some) with the same results (that is, i noticed my error after posting and before getting your response, then modified the script to reflect DiscussionController object).

    The interesting thing is that i DO get a handling if i use something like:

    public function PostController_BeforeDiscussionRender_Handler { yadda, yadda, yadda

    also

    public function Base_Render_Before {

    (and, yes, i did notice Todd's empty can of coke flying by...)

    However, in these cases, the sender's EventArguments is an empty array (I "var_dump" it).

    I forgot to mention that the function that my plugin 'rides on' is

    SearchController_Render_Before

    It is there that the script intercepts & interpret search criteria then implements the fire event and redirection. Should that make a difference? I wouldn't think so.

    Is there a way for me to attach the default.php i wrote for the plugin?

  • TimTim Vanilla Staff
    Answer ✓

    Do yourself a favor.

    At the place where you're trying to fire that event, put this:

    var_dump(get_class($this)); die();

    That class name is what you're firing on, and should be the prefixed controller name in your handler hook. I'm willing to bet it is not DiscussionController.

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • Thanks - I get the name of my plugin (in my case 'Zoomit').

    So, if i am not mistaken, the function in the defaul.php of my plugin where i hope to be able to see the EventArguments should be:

    public function ZoomitController_PopulateTitle_Handler($Sender) {

    ver_dump($Sender->EventArguments); die(); // This does not get executed..

    }

    It doesn't get executed either. Am I setting something incorrectly?

  • well did you define FireEvent('PopulateTitle') in your ZoomitController????????
    in your view or anywhere in your Zoomit Plugin??????

    There was an error rendering this rich post.

  • sahotataransahotataran ✭✭✭
    Answer ✓

    basically if you check /applications/vanilla/views/discussion/index.php you will find

    <?php $this->FireEvent('BeforeDiscussion'); ?>

    just an example. you will find same definitions through out Vanilla where EVENTS ARE DEFINED and their PLACE where they are to be fired.

    still if you dont get it then you can download and install http://vanillaforums.org/addon/eventi-plugin and it will tell you all the events that are fired and where it is fired when the page is rendered.

    hope that helps

    There was an error rendering this rich post.

  • sahotataransahotataran ✭✭✭
    Answer ✓

    if you just wanna change the TITLE of page then you can just use

    Base_Render_Before($Sender){
    $Sender -> Head -> Title('Your Title');
    }

    There was an error rendering this rich post.

  • BeyBey
    edited December 2011

    I think the main hurdle is my ignorance (of Vanilla, PHP, MVC, etc.)

    Let me explain what i am trying to do and where things go awry.

    I am trying to intercept the initial page load, the URL of which contains two strings.
    The first string identifies a category name (that i assume exists - if not nothing happens) - and the second is a part of discussion name supposedly within that category.

    I had a hard time implementing this on the DiscussionsControllers_Render_Before because I could not find the parts of my URL (I still don't know where to find the initial URL - where is it?).

    The URL i was trying to work with is:

    http://localhost/vanilla/index.php?p=discussions&Type=Order&ID=12345

    I was unable to find my parameters. So...

    The solution i came up with is to use the SearchController because there, i COULD find what i needed and it is working fine. The URL I am using is:

    http://localhost/vanilla/index.php?p=/search&amp;Type=Order&amp;ID=12345

    Note: The URL above is not shown correctly after the "?p=" in here (it should be "?p={percentsign}Fsearch{ampersand}Type=Order{ampersand}ID=12345.)

    The way it supposed to work (and it does for the main part) is to display a given discussion that exists and/or display a new discussion in the specified category if it does not exist yet. This is working great.

    Then, all I wanted was to pre-populate the discussion title (Name field) with the category (say Order) and the ID (12345) so the discussion title (not the page title) shows 'Order 12345' in the new discussion page - the case where the discussion does not exist yet. Simple, no?

    This is where things stopped working and i posted the question to which you and Tim responded and tried to accomplish it with the FireEvent mechanism that was suggested by Tim.

    All of the code is in two functions in the default.php of this plugin.
    The initial interception logic is implemented on SearchController_Render_Before from where i (successfully) redirect either to a particular discussion or to the post/discussion/{CategoryID}.
    The second function I am unable to get to work after using the FireEvent as either it does not get executed or the EventArguments array is empty.

  • sahotataransahotataran ✭✭✭
    Answer ✓

    did you check the eventi plugin???? i will tell you all events that are fired.

    second how i would do is using any hook eg Base_Render_Before you can print_r($Sender)

    this will show in browser all the detail of the $Sender - then in your browser you can look for TITLE i mean search for title it will show where it is in which array/object etc and then using a hook i would try to change the title dynamically as you are trying to do

    There was an error rendering this rich post.

  • BeyBey
    edited December 2011

    First, lots of thanks.

    I know how to change the field i need to change but i need to have something like the EventArguments to pass it in (but the EventArguments array is empty when i DO get my code to get executed).

    Instead of print_r, i am using var_dump - shows me everything i need to see (I think) - this is how i figured out where to get the parameters in the SearchController...

    I will now try to download the eventi plugin and, again, thanks so much.

    Still, where does one find the initial URL? I did not see it in the session or anywhere else. Still a mystery to me where that thing is tucked away...

  • sahotataransahotataran ✭✭✭
    Answer ✓

    i think all routing is done by bootstrap.php

    There was an error rendering this rich post.

  • TimTim Vanilla Staff
    edited December 2011 Answer ✓

    Tim said:
    You specify the firing object's full class name as the first piece, then the event name itself, then Handler

    The reason your event handler before (ZoomitController_PopulateTitle_Handler) did not fire is because you arbitrarily added "Controller" to the name of your object. Read my quote. You specify the firing object's full class name as the first piece.

    That means if get_class($this) returns Hamburger, your event handler for events your fire off of that object will start with Hamburger_, not HamburgerController_ or any other word.

    So if, as you say, it returned Zoomit, then you would handle events thrown off of that object with a handler method starting with Zoomit_.
    In your particular case, if you're doing $this->FireEvent('PopulateTitle'), you'd hook that event with a method called Zoomit_PopulateTitle_Handler($Sender)

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • TimTim Vanilla Staff
    Answer ✓

    As far as accessing the URL and its parameters, we have an object called Gdn_Request which does that. It has a ton of helper methods.

    You can access this object with the helper method Gdn::Request(). In your case, to get the value of the passed-in URL parameter 'Type', you'd do something like:

    echo Gdn::Request()->GetValue('Type')

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • TimTim Vanilla Staff
    Answer ✓

    Gdn_Request has many other parameters and methods though, so to get an idea of everything it stores, do a quick var_dump(Gdn::Request()); die(); somewhere and have a look. Then inspect /library/core/class.request.php

    Vanilla Forums COO [GitHub, Twitter, About.me]

  • Pile of thanks to both of you guys.

    The (new) bottom line is that i got my mini/pet project to work by using

    Gdn::Request()->GetValue(...);

    As well, I DID get into my plugin Event Handler but two things prevented me from using this apporach.

    First, the EventArguments array was empty, Second, there is no $Sender->Form where i can do SetFormValue.

    So... My approach was to introduce a parameter (a string that is the title i need) to the url I redirect to and then use Base_Render_Before code as the function where i reclaim what i need from the second instance of Gdn::Request()->GetValue(...);

    Not perfect - but working...

    I now have more time to reflect and see whether i can avoid Base_Render_Before.

    My next and last challenge is this mini project is to figure out how to disable the new discussion's title input field so the user cannot change it. If you have any ideas i am all eEars.

    Regardless, your tenacious help and patience are noticed and appreciated.

    Bey

Sign In or Register to comment.