i'm creating a website in PHP, that has Javascript elements as well. This one, will be having what is like a plugin system where a plugin can be dynamically added to the website.
I've created the plugin system, but i don't like certain elements of the design. In particular, i'm using an MVC pattern, but there is a problem with javascript abstraction.
I have a certain file that loads all the plugins. Then, there is a javascript file that dynamically adds boxes to a window, depending on the selection that was made, for what plugin should be used.
It goes like this in a js file :
if (SelValue == 'image_list')
image_list(form_name, new_div, parent_div);
if (SelValue == 'multiple_columns')
multiple_columns(form_name, new_div, parent_div);
Then, right below, follows the declaration of imagelist() and so on. Of course, this is pretty cumbersome and certainly does not look like a good practice. I would like to have all of these abstracted and isolated, so that a plugin is just a simple step to add to my code, if possible.
Do you know of any design pattern or practice that could fit this scenario ?
You could create a Object holding all functions like image_list and multiple_columns. Using those would look like:
plugins[SelValue](form_name, new_div, parent_div);
Adding a new function would look like this:
plugins.image_list = function (form_name, new_div, parent_div) {
/* … */
}
This definition could go in a different file. Is that what you meant?
Edit: Pretty single-file version:
plugins = {
image_list: function (form_name, new_div, parent_div) {
/* … */
},
multiple_columns: function (form_name, new_div, parent_div) {
/* … */
},
};
plugins[SelValue](form_name, new_div, parent_div);
If you're not going to have more than 2 IF statements, that would be sufficient. However, if you're going to be extending it (or could foresee it expanding), then I would suggest using a Factory/Command design pattern. Implementation details would include a table or a dictionary to replace the multiple IF-statements that you would have.
The module pattern would probably fit your needs very well. This pattern basically would treat each "plugin" as a module, which itself should be fully functional and independent. Then you have a module loader/controller, which is responsible for loading the correct modules, and enabling modules to communicate with each other.
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
http://www.yuiblog.com/blog/2007/06/12/module-pattern/
Related
I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).
I'm curious how plugins work, I just know that instead of changing the code we use plugins, but how do they do their job without changing the code ? and what should a coder consider when coding a new project so it can have plugins ? and thank you very much :D
There are multiple variations on how to implement a plugin system. Wordpress uses a quite common scheme often described as "hooks." I don't know the exact implementation but it basically works like this:
// plugin.php script registers its own callback function
register_plugin("hook_type", "plugin_function_123");
function plugin_function_123($params) { ... }
Where the hook_type is often an action name or something. And when the main application runs through a specific point (or e.g. needs some data processsed) it invokes all registered callback functions:
$output = call_plugins("hook_type", $param1, $param2);
This is often implemented behind the scenes as a simple loop:
foreach ($registered_plugins[$action] as $func) {
$func($param1, $param2, ...); // or call_user_func_
}
Now it depends on the hook/action type what parameters are present, and if any result text is expected. There are also differences in parameter passing (e.g. some callbacks require &$var references). And some plugin systems rely on objects instead (if not as many varying action types exist or more complex structures are to be worked with).
I'm creating my own script using the CodeIgniter MVC framework. Now, i want users to easily modify the site functionality and adding their own without modifying the code which i've already written.
How do i make my site pluginable ?
EDIT: The users would be the site admins. Not the end user. Basically just like drupal or joomla. Want the admin to be able to create/add plugins to extend site functionality.
There may be a better way that's specific to CodeIgniter, but this is what I would do:
First, create functions for various "hook points" in your code. Say, a function named PreArticle that you call in your code, before an article is displayed.
Allow the user to write code like this:
addHook_PreArticle('funcToCall');
function funcToCall( &$articleText ) {
$articleText = str_replace('Hello', 'World', $articleText);
}
addHook_PreArticle is a function you've defined, which would add the passed string to some internal list. Then when the PreArticle function is called, each of those functions are executed, passing in any appropriate parameters that you define.
Many CMS's Like Joomla and Blogs like Wordpress use variable function names:
$function="phpinfo";
$function();
You could load this into an array to create a list of functions that can be overridden.
That's a perfect case to use the Observer Pattern.
http://devzone.zend.com/article/5
The Smarty FAQ suggests one way to handle cacheable fragments, but it needs each page controller to do tons of work up-front, instead of encapsulating things properly.
We want to get to a stage where we can do something like:
<div id="header">
{our_categories}
{our_subcategories category=$current_category}
</div>
The output of the our_ prefixed functions should be completely cacheable, only relying on the named parameters (if any.) If we referred to {our_categories} in more than one template, they should all refer to the same cached content.
(it's probably worth mentioning that we tried using {insert name="..."} and coding up our own functions, but the results weren't cacheable, and we ended up hand-cranking the HTML retunred, rather than benefiting from Smarty's template processing.)
Our first crack at this uses a custom function smarty_function_our_categories, but the caching's going horribly wrong. Here's what our function looks like:
function smarty_function_our_categories($params, &$smarty) {
$smarty->caching = 2;
$smarty->cache_lifetime = 3600; # 1 hour
if (!$smarty->is_cached(...)) {
// ... do db access to fetch data for template...
$smarty->assign(....);
}
return $smarty->fetch(...);
}
The problem is: calling $smarty->fetch() within a function confuses smarty, making it lose track of which templates have insert-tags, and which don't. The end result is that Smarty forgets to replace certain markers when serving up cached content (markers it puts there to say: "replace this with the results of some non-caching {insert ...} call.) In our case, this manifests itself with our site showing a couple of md5 checksums and a php-serialized memento where our main menu should be - that's not good.
We assume we've made a mistake in how we're building our components, so the question finally becomes:
How do you safely create a caching component using Smarty to render itself?
You should not change caching parameters from inside Smarty function. Wheither or not the result of the plugin output is cacheable is defined when you register plugin.
http://www.smarty.net/manual/en/caching.cacheable.php
To create uncachable content inside cachable template just use {dynamic} blocks like this:
//Registering dynamic non-caching block with Smarty
$template->register_block('dynamic', 'smarty_block_dynamic', false);
function smarty_block_dynamic($param, $content, &$smarty) {
return $content;
}
I'm using CodeIgniter, and will likely use their template library as I want to keep things extremely simple to use. The content for the template variables will come from the database, but I want the business admins to know what content areas are available. Basically the names of the parameters when they choose a specific template. For instance, Joomla uses an extra XML file that defines each area, whereas Wordpress uses comments within a page template to inform the system that the PHP file is a template. I like the Joomla approach because you don't have to parse the PHP file to find the areas, but I like the Wordpress approach because you don't have an extra XML file associated with every template. Are there other approaches that I'm missing?
I think the nicest way would be to add a small hack to the template parser class. The code looks quite readable and clean in system/libraries/Parser.php. You could insert a hook in that class that can be used to keep track of the variables. I don't know, if it works, but here's a snippet:
class CI_Parser {
var $varCallback;
function setVarCallback($callbackFunction) {
$this->varCallback = $callbackFunction;
}
...
function _parse_single(...) {
$callback = $this->varCallback;
$callback($key);
}
...
//Somewhere in your code
function storeVarName($variableName) {
// Persist the variable name wherever you want here
}
$this->parser->setVarCallback('storeVarName');
You could do this directly in the controller:
// in the controller
print_r($data);
$this->load->view("main", $data);
Or a little more rudimentary, but you could pass to the template a PHP array of variables (or an object):
// in the controller
$data = array();
$data["namespace"] = array(
"title" => "My website",
"posts" => array("hi", "something else")
);
$this->load->view("main", $data);
And then in the view, have a flag to print_r the namespace to show all the vars available, so that business admins know exactly what to use.
// in the view
if(isset($namespace["showAllVars"])) print_r($namespace);
One option would be to call token_get_all on the PHP file (only when your business admins are loading it up), and parse the output of that.
The best approach, in my opinion, is to keep the variable definitions in another place (such as a database table, or a separate file). This will help with testing (i.e., a programmer can't just remove a tag and it's gone) and making sure things are still working as you move on with the application development in time.
Another advantage is that your application logic will be independent from the templating engine.
On a side note, if you expect a lot of traffic, you may consider using smarty instead. We have done extensive testing with most of the templating engines around and smarty is the fastest.