• This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.

Stuck With ControllerPublic & DataWriter For My Addon

TheBigK

Well-known member
#1
I'm a total n00b to XF development and PHP as well and stuck with the code. It'd be really nice if you could help with the following.

Addon Idea: 'Invite Me'
What It Does: Shows a button that says "I'm Interested", which when clicked, registers the Username of the user in a database table. If the user has already clicked the button, it will appear disabled with a message "You're already in the invite list".

The admin gets to see the list of all of the users who have clicked the button in the admin control panel as
[Username] |

What I've done so far:-

1. I began developing this addon by first creating the Route Prefix:-
PHP:
<?php

class InviteMe_Prefix_Route_Index implements XenForo_Route_Interface
{
    public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)
    {
        return $router->getRouteMatch('InviteMe_ControllerPublic_Index',$routePath);
    }
}
2. I then created ControllerPublic as follows [This one is messy and I'm stuck here]

PHP:
<?php

class InviteMe_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{

    public function actionIndex()
    {
        //Get InviteMeModel
        $inviteMeModel = $this->getModelFromCache('InviteMe_Model_Invite');

        //Get information about the visitor
        $visitor = XenForo_Visitor::getInstance();

        //Get the list of existing applicants
        $alreadyApplied = $inviteMeModel->getApplied();

        //Check if user's username already exists in our invite table
        if (in_array($visitor['username'], $alreadyApplied))
        {
            return $this->responseError('You are already in our waiting list.');
        }

        $viewParams = array($visitor['username']);
        return $this->responseView('InviteMe_ViewPublic_Index', 'inviteMe', $viewParams);

    }

    public function actionAcceptInvite()
    {
        //Make this a POST action
        $this->_assertPostOnly();

        //Get information about the visitor
        $visitor = XenForo_Visitor::getInstance();

        //Make this available for registered members only
        $this->_assertRegistrationRequired();

        //Create A DataWriter To Save The User ID To Database
        $dw = XenForo_DataWriter::create('InviteMe_DataWrite_Index');
        $dw->set('user_id', $visitor['username']);
        $dw->save();

        return $this->responseRedirect(XenForo_ControllerResponse_Redirect::SUCCESS, XenForo_Link::buildPublicLink('invite'));
    }
}
3. DataWriter:

PHP:
<?phpclass InviteMe_DataWriter_Index extends XenForo_DataWriter
{
protected function _getExistingData($data)
}
Questions:

1. I've been told that this one won't need a DataWriter and I can achieve saving of the username through a query. But, that'd be 'optimisation' :) . I wish to understand how to write this controller that uses a datawriter.

2. I've absolutely no clue about the $viewParams - and what should it have for the approach that I've adopted.

3.
//Get information about the visitor
$visitor = XenForo_Visitor::getInstance();

Why is it that I can't use $visitor defined in my actionIndex() in actionAcceptInvite(), without having to declare it again? Isn't it 'public' and should be usable throughout the class?
 

Chris D

XenForo developer
Staff member
#2
1. I've been told that this one won't need a DataWriter and I can achieve saving of the username through a query. But, that'd be 'optimisation' :) . I wish to understand how to write this controller that uses a datawriter.
I would always recommend using the DataWriter for everything. It should be clear from the existing XenForo code how these work, they are used extensively.

2. I've absolutely no clue about the $viewParams - and what should it have for the approach that I've adopted.
PHP:
$viewParams = array(
    'paramNameInTemplate' => 'value'
);
That would make {$paramNameInTemplate} available in the template with a value of 'value'.

Why is it that I can't use $visitor defined in my actionIndex() in actionAcceptInvite(), without having to declare it again? Isn't it 'public' and should be usable throughout the class?
No. In this context variables are only valid in the function in which they are defined
 

TheBigK

Well-known member
#3
That would make {$paramNameInTemplate} available in the template with a value of 'value'.
Yep, I got that already. But given the simplicity of the addon, I'm wondering if it's necessary to define the $viewParams? Can I end my action without setting up 'something' for the $viewParams?

No. In this context variables are only valid in the function in which they are defined
I wish to understand, why is it so?

Also, is it okay to define it twice like I did? If not - what's the suggested approach? I'm not sure if I should really have the action for button press (that is actionAcceptInvite() ) should be clubbed with actionIndex().
 

Chris D

XenForo developer
Staff member
#4
Yep, I got that already. But given the simplicity of the addon, I'm wondering if it's necessary to define the $viewParams? Can I end my action without setting up 'something' for the $viewParams?
View params can be an empty array. It passes variables from the controller to the template. $visitor is automatically available to templates therefore you should be able to just do:
PHP:
$viewParams = array();
I wish to understand, why is it so?
That's just how PHP works. Variables defined in functions are considered to be "local" to that function.

Also, is it okay to define it twice like I did?
Twice as in once in one function and once in another function? Of course. If you need the $visitor object in multiple functions, that's really the only viable solution.

I'm not sure if I should really have the action for button press (that is actionAcceptInvite() ) should be clubbed with actionIndex().
Either is valid, but you can indeed merge the two.

PHP:
if ($this->isConfirmedPost())
{
    // do stuff here if the action has been accessed via a POST request (e.g. submitting a form)
}
else
{
    // do stuff here if the action has been accessed via a GET request, (e.g. clicking a link)
}
 

TheBigK

Well-known member
#6
Update: I got back to developing this and think I did make some progress.

1. I decided to keep things extremely simple; and removed the check to see if user has already registered. I updated the ControllerPublic as follows:
PHP:
<?php

class InviteMe_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract
{
    public function actionIndex()
    {
        //Get Visitor Details
        $userID = XenForo_Visitor::getUserId();

        //Create a new dataWriter

        $dW =XenForo_DataWriter::create('InviteMe_DataWriter_Index');
        $dW->set('user_id',$userID);
        $dW->save();

        $viewParams = array();
        return $this->responseView('InviteMe_ViewPublic_Index', 'inviteme_index', $viewParams);
    }

}
2. Since I'm not going to need any of the DataWriter functions; and the system won't let me create an empty class; I decided to create empty functions -
PHP:
<?php
class InviteMe_DataWriter_Index extends XenForo_DataWriter
{
    protected function _getExistingData($data)
    {
//EMPTY 
    }
    protected function _getFields()
    {
//EMPTY
    }
    protected function _getUpdateCondition($tableName)
    {
//EMPTY
    }
}
Question: Does it all look 'okay' so far? I'm focused on being right than writing optimised code.

3. Next: ViewPublic.

Need some handholding here. Most of the addons I've seen seem to be rendering JSON. I've ZERO clue whether I should render HTML or JSON - and why? What exactly should viewPublic do - and how's it going to be different from a template? :sick:
 

TheBigK

Well-known member
#8
So you are not going with showing the form with the "Invite Me" button to the visitor first?
Umm, the workflow I've planned is this:-

1. User goes to -> domain/inviteme/
2. Is presented with a big ass button that says "Invite Me"
3. Clicks On the button -> and gets redirected to index page or home page.

I'm not performing any check on whether they have already registered in my first version. I'm not sure if my approach doesn't show the button the the visitor. :confused:
 

NixFifty

Well-known member
#9
Umm, the workflow I've planned is this:-

1. User goes to -> domain/inviteme/
2. Is presented with a big ass button that says "Invite Me"
3. Clicks On the button -> and gets redirected to index page or home page.

I'm not performing any check on whether they have already registered in my first version. I'm not sure if my approach doesn't show the button the the visitor. :confused:
I'm on my phone at the moment so I haven't been able to look in to detail.

Your actionIndex function looks like its handling the logic for item 3 in your workflow. So when you go to /inviteme the visitor's user ID will be logged straight away, without them clicking a button. Then they'll be sent to the index template.

Is that what you want?
 

TheBigK

Well-known member
#10
I'm on my phone at the moment so I haven't been able to look in to detail.

Your actionIndex function looks like its handling the logic for item 3 in your workflow. So when you go to /inviteme the visitor's user ID will be logged straight away, without them clicking a button. Then they'll be sent to the index template.

Is that what you want?
Hmm! I think I got the problem with my code. actionIndex will be automatically executed, right? So that'd quickly register the user and then take me to the index page.

I think I should create a different action. Say actionClickButton()?