Solar 0.20.0 released
Exactly one month after the last release, I have uploaded Solar version 0.20.0 alpha. There are lots of changes in this release, but the biggest news is that the page-controller now uses method-based actions (as opposed to file-based actions).
You can read the change notes here, but it's a really long list. If you want something a little more readable, with better narrative, you'll find it after the jump below. The new page-controller information is especially good. However, the narrative is quite long, so consider yourself warned. ;-)
Naming and Standards
About a third the change notes relate to naming, vocabulary, and standarization. Across the whole codebase, "drivers" are now called "adapters" in line with the Adapter design pattern. Similarly, I'm trying to use load() and is*() when appropriate, in line with the published coding standards.
Class Name Changes
Solar_User
The Solar_User subclasses have been brought to the top of the library, and their driver classes now have "Adapter" in their names.
- Solar_User_Access => Solar_Access
- Solar_User_Access_* => Solar_Access_Adapter_*
- Solar_User_Auth => Solar_Auth
- Solar_User_Auth_* => Solar_Auth_Adapter_*
- Solar_User_Role => Solar_Role
- Solar_User_Role_* => Solar_Role_*
Solar_Cache
The cache driver classes also now have "Adapter" in their name.
- Solar_Cache_File => Solar_Cache_Adapter_File
- Solar_Cache_Memcache => Solar_Cache_Adapter_Memcache
Solar_Sql
The SQL driver classes have been renamed to "Adapter" classes.
- Solar_Sql_Driver_* => Solar_Sql_Adapter_*
Inherited Config File Values
I have modified Solar_Base so that it looks at the Solar.config.php values for all its parent classes, and inherits them for the current instance. Say for example you have this inheritance hierarchy:
<?php
class Foo_Base extends Solar_Base {}
class Foo_Bar extends Foo_Base {}
class Foo_Bar_Baz extends Foo_Bar {}
?>
Let's also say you have this in your config file:
<?php
$config['Foo_Base']['zim'] = 'dib';
?>
When you instantiate Foo_Bar_Baz, its $_config will receive array('zim' => 'dib') from the config file, even though there is no 'Foo_Bar_Baz' config file key. This is because Solar_Base now grabs all parent config-file values and merges them automatically into child class config-file values. So unless you override them specifically for the particular child class, the parent values will be used. Parent values are merged top-down, so the immediate parent value takes precedence over grandparent values.
Note that this does not mean that parent class $_config property values are merged when inherited. The inheritance only applies to the values in the config file.
Adapter Configuration
Any class that uses an adapter now passes configuration to that adapter via a 'config' config key entry. For example, it used to be that Solar_Sql took a 'driver' key and passed config to that driver like this:
<?php
$config['Solar_Sql'] = array(
'driver' => 'Solar_Sql_Driver_Mysql',
'host' => '127.0.0.1',
'user' => 'username',
'pass' => 'password',
'name' => 'database',
);
?>
Now it uses an 'adapter' key and a 'config' key to indicate options passed to the underlying adapter object:
<?php
$config['Solar_Sql'] = array(
'adapter' => 'Solar_Sql_Adapter_Mysql',
'config' => array(
'host' => '127.0.0.1',
'user' => 'username',
'pass' => 'password',
'name' => 'database',
),
);
?>
Note also that you can leave out the 'config' key entirely, and the master class will use the default configs for the adapter:
<?php
$config['Solar_Sql'] = array(
'adapter' => 'Solar_Sql_Adapter_Mysql',
);
$config['Solar_Sql_Adapter_Mysql'] = array(
'host' => '127.0.0.1',
'user' => 'username',
'pass' => 'password',
'name' => 'database',
);
?>
Locale Files
It used to be that you could set the non-default location of a locale file using the 'locale' config key in in Solar_Base extended class. This is no longer the case. All locale files are always located in the "Locale/" directory in the class directory.
However, locale strings are now inherited; this means you can set locale keys in a parent class locale file, and the child class will use those values automatically (unless you override them in the child class locale file).
Page-Controller Class
Actions As Methods, Not Files
Previously, page actions were PHP files in the Actions/ directory (e.g., browse.action.php, read.action.php, etc). To map URIs to their actions, and to the parameters they needed, you used $_action_info with the action name as a key, and gave it a string of info paramters. For example, 'read' => 'id' would create $this->_info['id'] for you when the 'read' action was called.
In this release, actions are now methods in the page-controller class, and the parameters for that method are the info mapping. For example, the read action noted above would be "public method actionRead($id = null)". This makes the $_action_info mapping property unnecessary.
To migrate your action files to action methods, create one method for each file, and prefix its method name with the word "action". Then, for each $this->_info key you needed in your action, give the method a parameter. For example, if previously you had an "archive" action file, with info mappings for "year", "month", and "day", you could write a new action method like this:
<?php
public function actionArchive($year = null, $month = null, $day = null)
{
// ...
}
?>
The page controller maps URI action names with dashes and underscores to studly caps; e.g., "/example-name/" and "/example_name/" become "/exampleName/" internally, which maps to the "actionExampleName()" method.
This also means that once you copy your action files into methods, you can delete the Actions/ directory entirely.
Views
Views are now stored in a singular-name directory, "View/", not a plural-name "Views/". This is in line with the rest of Solar, where directories have singular names.
Views used to be named ".view.php" for full view templates, and ".part.php" for partial view templates. This has changed; the new standard is to name all templates with just a ".php" suffix, and if you have a partial template, you should prefix its name with an underscore. This is identical to the Ruby on Rails convention.
For example, if previously you had "Views/template.view.php" and "Views/partial.part.php", you would rename them "View/template.php" and "View/_partial.php".
Additionally, the page controller defines a series of fallback view-template paths automatically. For a Vendor_App_Example class, the page controller will look first in Vendor/App/Example/View/ for Example-specific views, then Vendor/App/View for views used across all apps. This lets you use standard error views, standard comment views, etc. among all your applications without having to copy and paste them into each application.
The creation of Solar_View instances for view processing has been separated into its own method, _viewInstance(). If you need fine-grained control over how a view instance is created, you can override this method and the page controller will use the returned Solar_View object for rendering your views.
The _viewInstance() method also defines where helper classes are located for you. For a Vendor_App_Example class, it used to that you only got Vendor_App_Example_Helper_* helpers. In this release, the _viewInstance() method defines more locations for helper classes: Vendor_App_Example_Helper for Example-specific helpers, Vendor_App_Helper for helpers across all applications, and Vendor_View_Helper for helpers across the entire Vendor codebase. (Solar_View_Helper remains the final fallback.)
Layouts
Layouts are now stored in a singular-name directory, "Layout/", not a plural-name "Layouts/". This is in line with the rest of Solar, where directories have singular names.
Layouts used to be named ".layout.php" for full layout templates, and ".part.php" for partial layout templates. This has changed; the new standard is to name all templates with just a ".php" suffix, and if you have a partial template, you should prefix its name with an underscore. This is identical to the Ruby on Rails convention.
For example, if previously you had "Layouts/threeCol.layout.php" and "Layouts/auth.part.php", you would rename them "Layout/threeCol.php" and "Layout/_auth.php".
Additionally, the page controller defines a series of fallback layout-template paths automatically. For a Vendor_App_Example class, the page controller will look first in Vendor/App/Example/Layout/ for Example-specific layouts, then Vendor/App/Layout for layouts used across all apps. This lets you override generic layouts for your specific application.
The creation of Solar_View instances for layout processing has been separated into its own method, _layoutInstance(). If you need fine-grained control over how a layout instance is created, you can override this method and the page controller will use the returned Solar_View object for rendering your layouts.
The _layoutInstance() method also defines where helper classes are located for you. For a Vendor_App_Example class, it used to that you only got Vendor_App_Example_Helper_* helpers (which are specific to the Example app, not to layouts). In this release, the _layoutInstance() method defines two different locations for helper classes: Vendor_App_Helper for helpers across all applications, and Vendor_View_Helper for helpers across the entire Vendor codebase. (Solar_View_Helper remains the final fallback.)
Hooks
Previously, there were only three "hooks" in the page controller: _setup() for extended constructor code, _preAction() to run before the first action, and _postAction() to run after the last action (but before view rendering).
Those hooks have been extended and modified to become:
- _setup()
- after __construct()
- _preRun()
- before first action
- _preAction()
- before every action
- _postAction()
- after every action
- _postRun()
- after last action
Note that _preAction() and _postAction() have been repurposed; if you currently have code in those methods, you should move it to _preRun() and _postRun().
Finally, there is a separate _render() method that handes view and layout processing; if you need to pre- or post-process rendered output, override that method. For example:
<?php
protected function _render()
{
$output = parent::_render();
// run $output through filtering process, then return
return $output;
}
?>