Digital Fashion

Rien de grand ne se fit jamais sans enthousiasme.

To content | To menu | To search

Zend Framework 1.5.1 PEAR package is available

A little late sorry, but ZF 1.5.1's package is now ready.

ruby: url_to_constant

A small bit of ruby to get a constant from an URL:

require 'uri'
def url_to_constant(url)
        return URI.parse(url).host.gsub(/^www\./, '').capitalize.gsub(/[^a-z][a-z]/i) { |m| m.gsub(/[^a-z]/, '').upcase }.constantize
end

Nothing exceptionnal here, just a pretext to post something.

Zend Framework 1.5 PEAR package is available

The long awaited 1.5 version of the Zend Framework has landed for some days already, and here comes its pear package. Please note the api version changed to 1.5 in this package.

Plugin "related by tags" pour dotclear 2, deuxième

Après de longs mois d'attente, le related by tags nouveau arrive enfin ! Au menu des réjouissances, une interface de configuration, ainsi qu'un widget font leur apparition. Vous disposez donc désormais de deux manières d'afficher les billets liés, directement en modifiant le template comme avant:

{{tpl:include src="_related_by_tags.html"}}

ou tout simplement en activant le widget correspondant, que vous pouvez configurer comme vous l'entendez. Bien sur, ce widget ne s'affichera que lors de la visualisation d'un billet.

Au chapitre des fonctionnalités / bugfix manquant(e)s, on notera le bug lié à l'utilisation de postgresql, ainsi que la traduction française, qui sera pour plus tard.

Encore une fois, n'hésitez pas à poster tous vos commentaires ici même.

Zend Framework 1.0.4 PEAR package is available

The package for the last 1.0.x release, 1.0.4, is now available on the phpmafia pear channel. Please report any issue in the comment of this post. The Zend_Locale's xml bug should now be fixed (they are now considered as php and thus put at the right place, which is not the best way to fix the bug I guess but at least it should work for now).

yaml, activerecord and acts_as_nested_set

I used to use this yaml_to_ar lib from christophe to load categories tree into my database, using acts_as_tree in the model that was perfect. Arrived the time when I felt the need to use acts_as_nested_set instead, for which I had to fill the lft and rgt columns. So I just rewrote the yaml_to_ar piece of code (put this in lib/yaml_to_ar.rb):

require 'yaml'

class YAML_to_AR

  def initialize(file, model)
    @data = File.open(file) { |yf| YAML::load( yf ) }
    @model = model
  end

  def process(data = @data, parent = nil)
    if data.is_a? Array
      data.each do |val|
        process(val, parent)
      end
    elsif data.is_a? Hash
      data.each do |key,val|
        parent = @model.create(:title => key)
        process(val, parent)
      end
    elsif data.is_a? String
      parent.add_child(@model.create(:title => data))
    end
  end

 end

This should handle both acts_as_tree and acts_as_nested. To ease things a bit further, I also wrote a rake task (to drop in lib/tasks/db_load_categories.rake for example):

namespace :db do
  desc "Loads categories defaults data"
  task :load_categories => :environment do
    require 'lib/yaml_to_ar'
    Category.delete_all
    categories = YAML_to_AR.new('db/categories.yml', Category)
    categories.process
  end
end

Now I just rake db:load_categories, and voila !

my first rails plugin: named_resources

It's a simple plugin (2 lines of code beside class and modules declarations) which allows routes created via the map.resources mechanism to be customized. Say you have the following map:

map.resources :members

It will generate routes like:

/members
/members/:id
/members/new

No say you want to i18n your app, in french for example, what do you do ? You just can't out of the box. This is where my plugin enters into action, just add a :route_name parameter to the map.resources call and you're set:

map.resources :members, :route_name => 'utilisateurs'

will generate routes like:

/utilisateurs
/utilisateurs/:id
/utilisateurs/new

It shall also work for nested resources, although I did not test that.

The code is actually pretty simple:

module ActionController
  module Resources
    class Resource
      def path
        route_name = @options.include?(:route_name) ? @options[:route_name] : @plural
        @path ||= "#{path_prefix}/#{route_name}"
      end
    end
  end
end

To install just use script/plugin:

script/plugin install http://tools.assembla.com/svn/riskle/rails/plugins/named_resources

or to install as an svn:external resource:

script/plugin install -x http://tools.assembla.com/svn/riskle/rails/plugins/named_resources

Accessing raw post data in a controller

For some reason, $HTTP_RAW_POST_DATA does not seem to be set inside an action controller. You'll have to use the php://input stream wrapper to access raw http post data:

$raw_post_data = file_get_contents('php://input');

Extending Zend_Controller_Router_Route: the singleton problem.

Today I ran into an issue while extending Zend_Controller_Router_Route. I wanted to add a little path pre/post processing in the match() and assemble() methods, so I just extended the Route class to add my tiny bits of code into the methods. Except it did not work at all. After a few debuging, it turned out that the Router uses Zend_Controller_Router_Route::getInstance() to retrieve a route object, which uses a new self(); statement to instantiate the route object. Problem is that self always refers to the current class definition we're in, if the method is called from a child class, without being overloaded, self will refer to the wrong class.

Example:

class Foo {
        public static function getInstance() {
                return new self;
        }
}

class Bar extends Foo {}

var_dump(Bar::getClass());

echoes something like:

object(Foo)#1 (0) {
}

Which is fscking wrong IMHO. A quick workaround is to overload the getInstance method, which is what I call pretty annoying as it does not follow the DRY principle.

How I use the Zend Framework

Having started a few applications using the Zend Framework, I came out with a few practices that I tend to use over and over. In this post I'll quickly expose some of them and explain why I do things the way I do them. As you'll notice, most of them are already widely known and used over the ZF developer's community. Please remember that these practices are just what I do, and come with no garanty at all to be best practices.

Continue reading...

Plugin "related by tags" pour dotclear 2

update: nouvelle version disponible

Allez hop, j'ai codouillé rapidement aujourd'hui un plugin dotclear 2 pour afficher une liste des billets ayant le ou les mêmes tags que le billet en cours de lecture par l'internaute. Il est téléchargeable dès maintenant sous forme d'archive tar gzipée ou directement de package dotclear. Pour l'utiliser, rien de plus simple, il suffit d'ajouter le tag suivant dans votre template, à l'endroit où vous souhaitez afficher la liste des billets:

{{tpl:include src="_related_by_tags.html"}}

Et voilà c'est tout :-)

Bon par contre, il faut faire gaffe a comment on tag ses billets (genre pour celui là j'ai preferré ne pas le taguer php histoire d'avoir des résultats plus pertinents).

ps: si quelqu'un connait un moyen d'éviter d'avoir a rajouter un bout de code au template je suis preneur (mais j'ai la flemme de chercher là tout de suite).

PDO not throwing an exception when it should

Today I ran into an issue that I already ran into a few weeks ago when I did not have time to dig up, but today I had this time (this plus it's a really annoying issue as you'll see). The main symptom is that PDO does not throws exceptions when you'd expect it to. It's very annoying. The reason, in my case, seems to be that I am querying an old mysql (3.23.x in my case but any 4.x will do according to this bug report). I was not able to find any info from google, so I'm posting this here so that people know :-)

New home for pagination component documentation

For those caring, I just posted some quick documentation for the pagination component at my assembla space. More docs will follow (including extensive phpdoc docblocks I hope).

Zend Framework 1.0.2 PEAR package is available

A PEAR package for the 1.0.2 version of the Zend Framework is now available from the PEAR PHPMafia channel. As usual, to install just issue the following:

pear channel-discover pear.phpmafia.net
pear install phpmafia/Zend

Bugfixes release of Zend Framework pagination component

I just released on riskle's assembla space a new version of my pagination component for the Zend Framework which you can download right now:

Riskle Paginate r122

This release fixes a nasty bug in Riskle_Db_Table::fetchCols which prevented from retrieving the right count of cols involved in the query.

The table component has also been slightly rewritten following Erik's suggestion to move the parent mapping into _fetch. The parent mapping itself has been improved to allow "multi level" table joining. This will be best explained with an example:

Say you have three table, Foo, Bar and Quux, and you would like to execute the following query:

SELECT * FROM Foo JOIN Bar ON Foo.bar_id = Bar.id JOIN Quux ON Bar.quux_id = Quux.id

This is now possible with the following mapping (in Foo's class of course):

array(
    'Bar' => array('local' => 'bar_id', 'remote' => 'id'),
    'Quux' => array('local' => 'quux_id', 'remote' => 'Quux.id'),
);

Easy heh ?

As usual, any comments are appreciated, and please note that this code is released under the same license as the ZF itself, the new-bsd license.

Of controller plugins and directory layout

When anyone on #zftalk ask about where controller plugins should be kept, we usually responds something like have your own library namesapce alongside Zend/ and put it in it like YourNamespace/Controller/Plugin/YourPlugin.php. But what about application specific controllers ? There's a time where you have to write a plugin that relies on the application at such a level that using it elsewhere would make no sense. In that case, where can we store this plugin ? The question arised this morning, and we ended up to the fact that having a controller-level plugin directory would not hurt, after all. So one could have the following directory layout (simplified on purpose):

/application/modules/default
    /controllers
    /library/Plugin
        MyPlugin.php

The drawback is that in order to use autoload you would have to have each modules Plugin dir in the include path, which is a bit of a hassle. Instead, we could have the much more simple folloing layout:

/application
    library/Controller/Plugin
        MyPlugin.php

Which is simpler but does not allow for modules specific plugins. Anyway, the former layout would require a bit more logic in the bootstrap in order to extract every modules path as plugins are registered pre-dispatch.

Hope it helps with directory layout organization :-)

UPDATE

What I've finally decided to do is the following:

/application
    library/App/Controller/Plugin
        MyPlugin.php

So that application specific code gets prefixed with the App_ namespace.

Zend Framework Pagination, third strike

UPDATE: new version (r105) available.

The component was given a little rewrite as expected, but maybe a little bit later than I would have wanted to :-) So it now has its own Rowclass proxy from which you can pull various infos such as current page, page range, next page, etc all exposed as getter methods (that is, getCurrentPage, getPageRange, getNextPage, etc), which you won't really have to worry about since the brand new view helper will take care of that for you. Usage has changed a little, bit, so let's first have a look at what's happening from the controller point of view:

$table = new Riskle_Db_Table_Paginate(new Table, $this->_getParam('page'));
$this->view->rowset = $table->fetchAll();

Not much changed here, except we don't need anymore to call getPaginationInfos(). Nice ! Now the big part, the view:

$this->paginate($this->rowset);
echo $this->paginate()->previous();
echo $this->paginate()->navigation();
echo $this->paginate()->next();

The view helper uses the neat composite helper trick from naneau - which is a really cool trick, great job naneau my fellow no-more-a-bunny. The first call inits the helper, feeding him the necessary rowset to work on, then you just have to call the methods you need to draw the navigation links. As you may expect, previous and next method will return nothing if no page is available (actually, they return their second argument, which defaults to an empty string).

Also, it's worth noting that the bundled Riskle_Db_Table features the patch from Erik, as well as a totally rewritten fetchCols method (now uses a straight Zend_Db_Select object instead of the ugly trick it used to use).

My code is no longer available on subversion, I moved the project to assembla. Instead ou can download this component from the riskle space's files board (direct download), the file contains all classes needed for the component to work, just unzip it in your include_path and you're set.

As usual, any comments are more than welcome.

Riskle_Form, a quick wrapup

So well, I've been busy these days working on my own implementation of a form component in the ZF spirit. This post is to help me see where I'm at with this component, as well as planning future evolution. I'll try my best to describe what it does and does not, and what it could do in the future.

Of course, while this is more of a personnal pense-bete than anything else, any comments are welcome.

Continue reading...

findBy{$Field} with Zend_Db_Table

A quick post to show how one can easily implement a findByField wrapper in Zend_Db_Table:

/**
         * Implements a simple findByField wrapper
         */


        public function __call($method, $args) {
                if (preg_match('/^findBy([a-zA-Z0-9]+)$/', $method, $parts)) {
                        $field = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $parts[1]));
                        if (!in_array($field, $this->_cols)) {
                                throw new Zend_Db_Table_Exception(sprintf('\'%s\' field not in row', $field));
                        } else {
                                $db = $this->getAdapter();
                                $where = $db->quoteInto($db->quoteIdentifier($field).' = ?', $args[0]);
                                return $this->fetchAll($where);
                        }
                }
        }

What it does is basically trapping any non-existant method call and check if the corresponding field exists, after converting CamelCasing to underscore_notation (eg: FooBar becomes foo_bar).

Zend_Db_Table and tables relationships

When developping a database tied application, you eventually come to a point where you get (at least) two tables with a parent/child relationship, such as for example a User table referenced by, say, a Post table (each post belonging to a specific user). That's basically the point where you need Zend_Db_Table relationships mechanism. the drawback of this mechanism is that, as far as I know, it does not produce joined queries to retrieve the parent data, but fires a query for each parent row. Thus instead of just using Zend_Db Relationships, I developped a simple yet effective auto-join mechanism that I called, in great simplicity, parent mapping. It supports multiple joins from multiple tables, remote fields specification and prefixing.

It is included in my db table class and you can see the interesting part of the code below for your convenience (Ignore the 3 first lines of the function as it is used for something else in my version of the framework).

Continue reading...

- page 1 of 8