Some tips I learned during plugin development
Paths
In a recent discussion I outlined that (sorry for the autoreference):
(plugins) use hardcoded directory name inside .php files
I say that since many plugins that I've seen use this approach:
include_once(PATH_PLUGINS.DS . 'MyPluginDirectoryName' . DS . 'class.myclass.php');
Not so clean, since you can use (beware of yourself):
include_once(dirname(FILE) . DS . 'class.myclass.php');
Views paths
When you need to set a view, you can use a similar approch:
$Sender->View = dirname(FILE) . DS . 'views' . DS . 'myview.php';
>
$Sender->Render();
or a (better? I found it just now) method form the class.plugin.php
$Sender->Render($this->GetView('myview.php'));
Note: Please contribute below with your own tips or with your critique.
Thank you to @hgtonight for this discussion.
There was an error rendering this rich post.
Comments
Great post - glad you're learning new stuff about plugin development! If I may, I have a few things to add:
Paths
First off, relying on
__FILE__
or any derivatives of$_SERVER
variables is a bad habit. I learned this the hard way when I started working for Vanilla - things can go awry when moving code with$_SERVER
variables from one environment to another.The first, and probably foremost, reason is that not all web servers provide you with
$_SERVER
variables and some web servers may only provide you with some and omit others. The second reason is that$_SERVER
variables are pretty oblivious to symlinks: These are automatically resolved, thus in most cases resulting in unreachable files.Lastly, I personally "never" (exceptions occur) use any
include
orrequire
statements in my code. I rely solely on autoloading and would usually write my ownspl_autoload_register
functions before discovering how to include stuff directly in the Garden autoloader - http://vanillaforums.org/discussion/23402/how-to-register-files-and-folders-in-the-garden-autoloader-2-1View paths
Again, the stuff I wrote about
$_SERVER
vars also applies here. I'm not sure if the following is the case in 2.0, but this is how I include views in my 2.1 plugins:This would fetch the following view (
plugins/FooBar
andbarfolder
can be omitted in many cases):plugins/FooBar/views/barfolder/fooview.ext
whereext
can be eithertpl
orphp
.I think that was my 50 cents!
Kasper Kronborg Isager (kasperisager) | Freelance Developer @Vanilla | Hit me up: Google Mail or Vanilla Mail | Find me on GitHub
Interesting discussion, here are some of my coding habits.
Paths
Regarding paths, I tend to define them as constants, together with several other values, because I tend to reuse them multiple times and. If I choose to change them while refactoring, it's easier to have them all declared in one place.
This said, I also rely mainly on the autoloader for loading the required files. The only exceptions are one or two files that I normally load manually (e.g. the one with the definitions described above), for which writing a full autoloader would be overkill.
Definitions/Constants
More of a general habit than a specific Vanilla best practice, I avoid using magic strings and magic numbers as much as I can, relying on constants for classes' internal values and global definitions for general values, such as error codes. All values are prefixed with the plugin name, to avoid clashes, and they become available to other plugins as well. Since many of my plugins are interdependent, that comes handy.
Testing
I finally found a way to run Unit Tests against Vanilla plugins using ANT and TeamCity. Although a bit rough, it works (after countless hours of WTF, I must add). I hate manual testing as the next Developer, therefore having a machine doing them for me is a life saver. It would be nice to have official guide on Continuous Integration for Vanilla, but I will eventually write one myself when I'll be happy with the process.
Documentation
Writing documentation is one of those tasks that many Developers dislike, but, as I wrote many times, it's part of a Developer's job and it must be done. To be more specific, there are two types of documentation that must be written:
Also, all methods and functions should be properly documented with JavaDoc comment blocks.
I worked on a project where the Lead Developer (who openly declared himself a master at all disciplines listed in his CV) declared every method, in every class, as public, simply because the class hierarchy (entirely conceived by him) was a complete mess and he kept getting method access issues. Having classes with dozens of undocumented, public methods named like
ft_file_add()
, with variables and arguments likeaa
,at
,bt
is not a good way to keep track of what your code does.My shop | About Me
My key take away from this is that you don't need to include module files IF you place it in the
modules
folder and call it class._custom_module.php.Search first
Check out the Documentation! We are always looking for new content and pull requests.
Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.
In my experience, you can place the files wherever you like, as long as you call the file
class.yourclassname.php
. This applies to Modules, Models and almost any class name (e.g.class.thisisafancyclass.php
would be automatically loaded too).Controllers are handled slightly differently, as it seems that the Autoloader looks in specific folders for them. I seem to remember that @kasperisager posted a way to register your own controllers, or something like that. When I developed my Bagdes plugin, I took a shortcut, and avoided using the word "Controller" in my classes. A bit dirty, but it works.
My shop | About Me
Remember functions.general.php
Sometimes I need to get some specific information or to transform data...and I must write many lines of code. But many useful functions to do that are already in functions.general.php...
A comprehensive list from 2.1
Wait, not only you can use them
But you can override them in
/conf/bootstrap.before.php
, changing the way vanilla works (since these functions are used in the entire codebase). They are loaded conditionally from functions.general.php and if you re-define (e.g.)function T($Code, $Default = FALSE)
in your bootstrap file, your version will be used.There was an error rendering this rich post.
Adding controller actions from a plugin
It's an easy task, just add a function to the plugin class:
But, wait...
We need to take care of other plugins hooks. Because an existing plugin that have a
ProfileController_BeforeRenderAsset_Handler
can rely, for example, on the$Sender->User
field (I ALWAYS useGdn::Session()->User
), since it is present in the ProfileController. But when the hook is called for the new action, the field isn't set, and this can generate many errors in other plugins.So, what to do?
Just analyze controller's fields and take care to provide all public fields. In this case (ProfileController):
How can I encounter this error?
Developing a plugin for a client (will be released soon) that also uses @peregrine PeregrineBadges :-). See
ProfileController_BeforeRenderAsset_Handler
function in the plugin.There was an error rendering this rich post.
not sure what you mean, in this case sender-user was not the same as sesssion user, they were two different entities from what I recallm and two different userids. I didn't re-analyze it,
I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.
Custom smarty tags from a plugin, an application or a theme
Define your custom tag(s) like this (vanilla searchbox) in a file called function.mytags.php
Then just add this function to your plugin class file or in your application
class.hooks.php
or in your themeclass.mythemethemehooks.php
.Now you can use
{supersearch}
tag directly from your default.master.tpl file (or from any other tpl file).There was an error rendering this rich post.
Adding a js file from application's class.hooks.php
Application folder: /applications/myapp
Js file: /applications/myapp/js/my.js
Just read Controller::RenderMaster() source code
There was an error rendering this rich post.