Creating an Addon.

@Nelson T. you should also consider purchasing a book on PHP/MySql. It's pretty convenient for referencing. I have one I purchased a few years ago, and still look things up with it instead of searching on the internet; it's faster, and gives you the exact answer (or example) you are looking for, :)

There are hundreds. Which one do you like to use?
 
@Lawrence

As this tutorial is from 2010, is your tutorial up-to-date? As I want to tackle the creation of add-on creating and I was wondering if I could really use this tutorial step-by-step as it seems many things changed in the past years.
For example you state here, that event hints were introduced later.
Maybe, is it too much to ask to update the tutorial?

2nd question.
Besides basic php knowledge, OOP-knowledge and MVC-pattern, what else do we need to comprehend before we can create our own add-ons (ofc small size)?
 
@sbj it's still relevant. The only thing, IMO, that needs to be included are data writers and prepared statements. In this late stage of XF 1.x series I doubt that this tutorial will be updated. As for XF 2.x, I would like to write a new tutorial, but I doubt it will be official as I am no longer part of staff here, but you never know, this kind of straight forward tutorial is needed.

what else do we need to comprehend before we can create our own add-ons

Three things off the top of my head. I think if I had to pick the most important one to focus on, it would be responses. You can dump a response out, and sometimes it would appear to be nothing there, when actually there is, you just need to know what to look for and that information is provided to you in the relevant controller. You need to be aware of when your code should be executed, ie: view, and $response has that information.

The second thing would be data writers. You can do a lot with them, from manipulating the data in the pre-save, to executing your code after the post-save. You don't need a data writer for everything. A simple update for a member, for example, can be handled by a model.

The third thing would be where your add-on should tap in to XF to be executed. The guaranteed place is the front controller pre-view. You can accomplish most everything there and it (may) contain a wealth of data from the type of view, to breadcrumbs and everything in between, but is it practical? It just depends on where your data is going to be used. If on almost every page, then yes, if only on a member's profile, then no.

One more little thing that I am always aware of, calling phrases:
If you call an uncached phrase in your code it is going to cause a query. No big deal, unless you need a few of them loaded, each may cause a separate query, so make sure all phrase objects that you need are grouped together. For example:

$myNeededPhrase1 = new XenForo_Phrase('myphrase_hello_world');
$myNeededPhrase2 = new XenForo_Phrase('myphrase_hello_universe');

The above takes one query to retrieve both objects, and yet:

$myNeededPhrase1 = new XenForo_Phrase('myphrase_hello_world');
some code here to do something nifty, and then...
$myNeededPhrase2 = new XenForo_Phrase('myphrase_hello_universe');

will take two queries. When it comes to unnecessary queries, I'm a little anal on that and so always watchful, :confused:
 
  • Like
Reactions: sbj
Thx for answering.
Yeah, the XF2 is overdue by now as the whole community stopped developing stuff cause of the need of a rewrite anyway.

Can you explain what data writers and prepared statements are? Or tell me under what categories in a book I can find those terms explained?

Personally, I have already the understanding for functional basic PHP and OOP. Now I'm looking into MVC pattern, which is basically just a structure for "who takes what job and how to connect those together". But I started reading about that newly but it seems it will not be that hard to understand.

What I don't see explained are listeners anywhere. Any tips about that?

When I look into some XF add-on codes, I get the idea and the execution of the code. But my biggest problem is those defined keywords used in the code. I don't see anywhere explained how people come to the conclusion to use that stuff. Like how you suddenly now the right table in the database. Or how you know suddenly the correct arguments to use in a function. For example. This line
Code:
public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)

I just don't see the "big picture" yet.

Anyway, thanks.
 
Data writers are used to verify data. For example, when you are registering on a forum and you do not fill out a required field, the data writers preSave method will throw the error, and no data is saved until all the fields are filled out correctly.

Prepared statements are for databases. It is the first line of defence against any mySql injection attempts. They ensure that arguments are properly escaped, quoted, etc. Although technically it is not required to use prepared statements, it's in your best interest to do so as they are all about security.

Listeners, are like events at the Olympics. Athletes don't move until they hear the gunshot for their particular event. XF has 34 events that you can listen for the gunshot. Once it is heard, let's say for the file health check event, all the athletes (add-ons), for that event react (fire).

Which listeners your add-on uses depends on what your add-on is going to accomplish. For example, if your add-on does not add in another navigation tab, then you do not need a navigation_tabs listener. If it does then you will. One of the most common listeners would be the load_class_model listener. If your add-on extends the user model for example, whenever the user model is loaded, your add-on's file that extends the user model class gets loaded so that you can call your methods from your add-ons controller. The hint here would be XenForo_Model_User. Your listener would not be loaded until the user model is.

public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router)

The above contains the path (URL), any action that is going to be done, and any arguments. For example, when you are about to view a member's profile, the action is members and the argument is the users name and id. /members/lawrence.219 The above looks at the request, finds what should be the action and sends the relevant data to it's matched controller. Here is a good spot for you to use Zend_Debug::dump(), to see what data $routePath/$request/$router contains, and when it does contain something.

Hope this helps.
 
Thank you. It helps definitely. Now I understand the concept of listeners and know for what datawriters are and where to use prepared statements.
The journey got a bit easier now.
 
Hey,

me again. So I completed your tutorial (created the addon), which is basically a step-by-step tutorial with copy and paste.

I have some questions.

You say in the beginning of your tutorial:
As the purpose of our Add-on is to not allow new members to edit (create) a signature, we must locate where members can edit their signature within XenForo, so we can Listen for the class being called. This is handled by a Controller. There are four Controller directories: Admin, Helper, Public, and Response. Editing a signature is not an Admin function. Nor is it a Helper, or a Response, that leaves us with Public. From your filemanager navigate to ControllerPublic. As editing a signature is part of a members account look for Account.php. Open this file and search for signature. The first word highlighted is just above: public function actionSignature(). This is the function we want to override.

How do you know in the first instance that there is a function called actionSignature()?
How do you know in the first instance, that there is this function and that you need to extend/override it?
Why didn't you went with the approach by creating your own function, instead of looking for an existing one which you can extend/override?



In Part 4 of your tutorial, you do:
Before we select Save, we need to create our PHP Callback Class and Method to render a list of groups for Admins to choose from.
by writing this code.
Code:
<?php

    class LimitSig_Option_Group
    {

        public static function renderOption(XenForo_View $view, $fieldPrefix, array $preparedOption, $canEdit)
        {

            $preparedOption['formatParams'] = XenForo_Model::create('LimitSig_Model_GetUserGroups')->getUserGroupOptions(
            $preparedOption['option_value']
            );

            return XenForo_ViewAdmin_Helper_Option::renderOptionTemplateInternal(
            'option_list_option_checkbox',
            $view, $fieldPrefix, $preparedOption, $canEdit
            );

        }

    }
What I don't know is how you know how to write this code.
You are declaring a static function with the name renderOption.
But how do you know which function arguments you need to use? How do you know that one of your arguments is "XenForo_View $view"?
(side question here, the variable $view is an object, right? Whatever is passed to this function to the "$view" variable, the type of it must be the "XenForo_View" object, right?)

I mean I started to understand of how everything is connected. But I don't understand how you people know the correct names for parameters, class names, argument, etc.
 
i test it.but a problem happen ,the problem was Callback LimitSig_Listener_LoadClassController::loadClassListener is invalid (Invalid Class).why?
 

Attachments

  • 123.webp
    123.webp
    5.4 KB · Views: 10
Hey,

me again. So I completed your tutorial (created the addon), which is basically a step-by-step tutorial with copy and paste.

I have some questions.

You say in the beginning of your tutorial:


How do you know in the first instance that there is a function called actionSignature()?
How do you know in the first instance, that there is this function and that you need to extend/override it?
Why didn't you went with the approach by creating your own function, instead of looking for an existing one which you can extend/override?



In Part 4 of your tutorial, you do:

by writing this code.
Code:
<?php

    class LimitSig_Option_Group
    {

        public static function renderOption(XenForo_View $view, $fieldPrefix, array $preparedOption, $canEdit)
        {

            $preparedOption['formatParams'] = XenForo_Model::create('LimitSig_Model_GetUserGroups')->getUserGroupOptions(
            $preparedOption['option_value']
            );

            return XenForo_ViewAdmin_Helper_Option::renderOptionTemplateInternal(
            'option_list_option_checkbox',
            $view, $fieldPrefix, $preparedOption, $canEdit
            );

        }

    }
What I don't know is how you know how to write this code.
You are declaring a static function with the name renderOption.
But how do you know which function arguments you need to use? How do you know that one of your arguments is "XenForo_View $view"?
(side question here, the variable $view is an object, right? Whatever is passed to this function to the "$view" variable, the type of it must be the "XenForo_View" object, right?)

I mean I started to understand of how everything is connected. But I don't understand how you people know the correct names for parameters, class names, argument, etc.

Hi, to answer your questions:
When I first learned about actions I looked at the URL in the browser. For example, editing a signature the URL contains account/signature, signature is the action. All action methods in XF controllers are prefixed with the word action, so in this case actionSignature.

For the renderOption, I learned what to do by examining pre-existing ones in the viewAdmin/viewPublic directories.

I just learned it overtime by, as Mike suggested to me at the time, was step through what happens when an action is performed. That is why this tutorial was written that way; it was to step you through what is happening when someone edits a signature, :)

i test it.but a problem happen ,the problem was Callback LimitSig_Listener_LoadClassController::loadClassListener is invalid (Invalid Class).why?

Have you verified your directory structure reflects the execution call'backs class, and in that class the method (loadClassListener) is correct? That's the only thing that would cause that error.
 
Is the tutorial mentioned in the OP still relevant as of today or is it outdated? Only asking because I plan on learning how to build an add-on and want to make sure I'm on the right path.
 
Format Parameters. You can set limits and behaviors to certain types of options. As we are using a spin box we can add to this option a min value, max value and a step value. Any value below the min, will default to the min value, and any value inputted above our max value will default to the max value. Just place each one you want to define on it's own line. The step value sets how much pressing the + or - will increase the value in the text box. Remove the step=x line if you want a step of 1.
Min=0
Max=50
Step=5

The Format Parameters must be lowercase otherwise they're ignored.
min=0
max=50
step=5


Excellent tutorial, thanks!
 
Top Bottom