We all do it. Keyword research, CTR analysis, etc and figure out our perfect keyword. Put it in the <title> – and watch. After enough data has been captured.. we repeat, adjust, etc. Eli on his SEO Blog wrote a blog about how to Automate SEO with Page Titles. This approach is very affective. So for all your WordPress lovers, I wrote a plugin that does this.. for you. You configure the trigger number, (how many keywords to log before the plugin kicks in) & the keyword delimiter  ( -, |, ::, etc.) and the plugin does the rest. It analyzes the keyword from the popular search engines, stores them and auto adjust the page titles based on the highest traffic generating keywords. I called it, Poly Title WordPress Plugin and put it on my new SEO Tools website.

Check it out.


PingCrawl 2

17Jun09

This is a follow up to PingCrawl which you can read about in full detail there. PingCrawl v2 fixes some of the main issues people reported including:

  • php4 support
  • more shared hosting friendly
  • wp 2.7 / 2.8 support
  • better hook management
  • admin management
  • auto blogging support
  • seeding capability with manual override on custom tag
  • if no tags are present on the post to use the title for the blog query (fixed in 2.3)
  • feed protocol support for some shared hosting
  • fixed simplexml object error rss (fixed in 2.4)
  • integrity check to make sure all pingback links are unique and not re-used on other tags. (pending)

I am also planning out and building the admin interface to PingCrawl which should make this plugin more manageable.

Please leave all feedback, bugs – things that work well – things that don’t work well – and other comments in here. I will try to release the improved versions once a week for the next 3 weeks.

For those who are on php4 you will need the simplepie plugin, http://wordpress.org/extend/plugins/simplepie-core to use.

Download PingCrawl 2.08 (beta release)

Please note that the current build is in alpha. If you are using it, you need to unzip the directory into your plugins directory in WordPress. Then activate PingCrawl v2.


So, I personally am a huge fan of the doctrine project. And, seeing as I am using Zend Framework I utilize Zend_Paginate in my service layer. All that being said, let me introduce you to my Doctrine Paginator Adapter:

<?php
/**
 * Custom Adapter for Doctrine to use with Zend_Paginator
 *
 * @author Josh Team - http://joshteam.wordpress.com
 * @version 1
 */
class SpottedHere_Paginator_Adapter_Doctrine 
                 implements Zend_Paginator_Adapter_Interface
{

    protected $_query;

    public function __construct(
                 Doctrine_Query_Abstract $doctrineQuery)
    {
        $this->_query = $doctrineQuery;
    }

    /**
     *
     * @param integer $offset Page offset
     * @param integer $itemCountPerPage Number of items per page
     * @return array
     * @see Zend_Paginator_Adapter_Interface::getItems()
     */
    public function getItems ($offset, $itemCountPerPage)
    {
        $this->_query->offset($offset);
        $this->_query->limit($itemCountPerPage);
        return $this->_query->execute();
    }
    /**
     *
     * @see Countable::count()
     */
    public function count ()
    {
        $query = clone($this->_query);
        $query->select('count(*) as total');
        $query->limit(0);
        $query->offset(0);
        $rs = $query->fetchOne(array(), Doctrine::HYDRATE_ARRAY);
        return $rs['total'];
    }
}
/* ---- THEN TO USE ---- */
    protected function queryToPaginator( 
           Doctrine_Query_Abstract $query, int $itemsPerPage = null)

    {         $paginator = new Zend_Paginator( 

           new SpottedHere_Paginator_Adapter_Doctrine($query) );
        $paginator->setItemCountPerPage( 
           ($itemsPerPage) ? ($itemsPerPage) : self::PAGINATOR_ITEM_PER_PAGE );
        $paginator->setCache(SpottedHere_Cache::factory());
        return $paginator;
    }

PingCrawl Version2 Can be found Here

So, I was working with Eli about a cool & helpful plugin that we could develop & use for our projects and we { more he than I } came up with the idea of PingCrawl. Basically here is the simple theory of operation:

Theory Of Operation

  • The plugin will listen to anytime a post is saved, published, updated, etc.
  • The plugin on execution time will find all the tags on the post and perform the following per tag:
    • Use Google API to check for ( 35 ) results with the tag name.
    • With the ( 35 ) results it loops through them and performs the following
      • Does the result have a pingback meta tag? 
      • Does the result have trackback somewhere in the source
      • (if yes to both) it stores the pingback xmlrpc location in memory.
      • (if no to either) we skip that record and move to the next.
      • Once their are 5 legit pingable servers we then append their links to the post we currently added.
      • We then retrieve the xmlrpc urls from memory, and execute a pingback.ping against the xmlrpc as defined in the pingback 1.0 spec. (due to the nature of pingbacks and php it is not a 100% guarantee. A lot of dependencies on state, server responses, headers, etc.)

Their are built in features such as caching google’s recordsets per tag, so you don’t have to make request out to google for the same use. And logic to know if you’ve already “PingCrawled” a post then on edit to ignore it, etc w/ a built in polling system.

Installation:

  1. Download Plugin
  2. Put file in the wp-content/plugins directory of your wordpress installation
  3. Login to your blog dashboard
  4. Click on Plugins
  5. Click on Active to the Right of PingCrawl in the list
  6. Make a Post
Note ( because of the nature of the script any one tag can make as many as 41 HTTP request and storing source code into memory to run regular expressions against. Because of this I would try to limit my tags to no more than 3 (123 HTTP Request). Use more at your own risk.
This is very beta, very my first wordpress plugin, etc. etc. So I’d love any suggestions, feedback, concerns, etc. 

TODO

  1. Some ping-backs aren’t going through that should, need to make sure algorithm isn’t too tight.
  2. php4 compatibility.
    1. Write alternative XML parse lib for php4.
  3. If no tags, auto populate tags based off content of post.
  4. Check current php settings for file_get_contents if not allowed use cURL.

Update

Their is a newer version of PingCrawl which you should use, Eli posted an older version on his blog. Silly Goose that he is!

Download Pingcrawl 1.02 (php4 friendly)

PingCrawl Version2 Can be found Here

Also I wrote this on php5+ assuming you are using it too. If you are using php 4 let me know, I’ll try to port it over in the next couple of days. Hope this helps some people!

Revisions:

  1. 8/6/2008 – Made Pingbacks more stable and secure, with higher probability of success
  2. 8/6/2008 – Reduced seeding from 1/6 to 1/11 ratio.
  3. 8/7/2008 – Attempt #1 at php4 Friendly

PingCrawl Version2 Can be found Here


So in my latest project I’ve been using Zend_Db_Table which is really nice for interfacing with a table, however it lends itself for a simple table structure which maps perfectly with your php/application model. I, however, do not have that luxury as I usually normalize my table structure. So one night, around 2am, I wrote an abstract class to allow me with minimum code do the following:

$table1->joinedTable->joinedTableField

Now, I am sure the code I wrote to do this is coupled, inefficient, etc. So as always, feel free to openly torment it :)

First Things First
Let me lay out my file structure that I use. The file structure is not dependent on the class I wrote, but I use autoload so it will make sense why I name what I name what:

+ app
  + Models
    + DB
– AbstractModel.php (this is my class)

So I basically added another layer to the Model. In /app/Models/DB I put all my simple Zend_Db_Table classes that represent a table in my database. Then I put all my PHP Model Objects in the /app/Models directory. Before I get to far ahead of myself, it’s always best for me to learn by a live working example so without further adue:

The Example

I have about 16 tables so far in this project ( mid build ) but let’s take a table that joins a lot of other tables. That table is called establishments. Here is the DDL for the table establishments:

CREATE TABLE `establishments` (
`id` bigint(20) unsigned zerofill NOT NULL auto_increment,
`name` varchar(255) default NULL,
`categoryID` mediumint(8) unsigned default NULL,
`longitude` double default NULL,
`lattitude` double default NULL,
`address` varchar(255) default NULL,
`cityID` tinyint(3) unsigned default NULL,
`stateID` int(10) unsigned default NULL,
`zip` varchar(6) default NULL,
`website` varchar(255) default NULL,
`mainPicID` int(10) unsigned default NULL,
`description` text,
`dressID` int(11) default NULL,
`vallet` tinyint(1) default NULL,
`isActive` tinyint(1) unsigned NOT NULL,
PRIMARY KEY  (`id`),
UNIQUE KEY `name` USING BTREE (`name`,`categoryID`),
UNIQUE KEY `address` (`longitude`,`lattitude`)
) ENGINE=MyISAM AUTO_INCREMENT=24 DEFAULT CHARSET=utf8

The columns to note are:

  • categoryID: categories Table ( id, name, isActive )
  • cityID: cities Table ( id, name, lattitude, longitude, isActive )
  • stateID: states Table ( id, name, longitude, lattitude, isActive )
  • mainPicID: photos Table ( id, categoryID, entityID, userID, location, description, timestamp, isActive )
  • dressID: dress Table ( id, name, description, isActive

The Objective
The goal is to be able to do the following very easily:

$establishment->name //returns establishments.name
$establishment->category->name //returns categories.name mapped to categoryID
$establishment->city->name //returns cities.name mapped to cityID
$establishment->state->name //returns states.name mapped to sateID
$establishment->photo->location // returns photos.location mapped to mainPicID
$establishment->dress->name // returns dress.name mapped to dressID

As you can see this gives us two things, easily mapping our normalized table out & being able to keep naming in our application layer different then the naming in our database (e.g. as a property that represents a table is usually singular tense and the table is plural). Well, let’s as always, work backwards. We need to load $establishments:

//I actually do not do this, I use a factory! But here you go anyways
$establishments = new Models_Establishment();

//Grab the Active Record Where the Name Matches the var $estabNam
$establishment = $establishments->find('name', $estabName);

The Establishments Class
Now we need to actually have our Models_Establishment class. Simply save the following in /app/Models/:

class Models_Establishment extends Models_AbstractModel {
  protected $_map = array(
    'self'=>'Models_DB_Establishment',
    'category'=>array('col'=>'categoryID', 'class'=>'Models_DB_Category'),
    'city'=>array('col'=>'cityID', 'class'=>'Models_DB_City'),
    'state'=>array('col'=>'stateID', 'class'=>'Models_DB_State'),
    'photo'=>array('col'=>'mainPicID', 'class'=>'Models_DB_Photo'),
    'dress'=>array('col'=>'dressID', 'class'=>'Models_DB_Dress')
  );
}

If you notice the first thing this class does is extend the abstract model class which I haven’t gotten into yet. The only other thing in this class is the $_map array which simply maps your object. The ’self’ index maps the current object to it’s base/core Zend_Db_Table file as you see here ‘Models_DB_Establishment’ which exist in /app/Models/Db/Establishment.php and it looks like:

class Models_DB_Establishment extends Zend_Db_Table_Abstract{
  public $_name = 'spottedhere.establishments';
  protected $_primary = 'id';
}

Nothing fancy there. Then the other indexes are category, city, state, photo, and dress. These all map what the ID is in the current object (categoryID, cityID, etc.) and which DB file to use. All these DB files exist in /app/Models/DB and here is what /app/Models/DB/Category.php looks like:

class Models_DB_Category extends Zend_Db_Table_Abstract{
  public $_name = 'spottedhere.categories';
  protected $_primary = 'id';
}

As you see they are all very similar. So for the most part all you have to do is have a /app/Models/DB/ table for each table you have and each one just specifying the name of the table and the primary key. Now all you need is the abstract class which exist in /app/Modles/AbstractModel.php and it looks like:

abstract class Models_AbstractModel {

  protected $id;
  protected $data;
  protected $selfObject;
  protected $_complexFields;
  protected $_map;
  static protected $pk = 'ID';

  public function __construct() {
    $this->selfObject= new $this->_map['self']();
  }

  public function __get($what) {
    if(is_array($this->_map)) {
      if(array_key_exists($what, $this->_map)) {
        try {
          return $this->data->$what;
        } catch (Exception $e) {
          return false;
        }
      }
    }
    if(is_array($this->_complexFields)) {
      if(array_key_exists($what, $this->_complexFields)) {
        $o;
        foreach($this->_complexFields[$what]['fields'] as $field) {
          $o .= $this->data->self->$field.$this->_complexFields[$what]['seperator'];
        }
        return $o;
      }
    }
    try {
      return $this->data->self->$what;
    } catch (Exception $e) {
      return false;
    }
  }

  protected function build() {
    if(is_numeric($this->id)) {
      $this->data->self = $this->selfObject->find($this->id)->current();
      if(is_array($this->_map)) {
        foreach($this->_map as $field=>$properties) {
          if(is_array($properties)) {
            $s = $properties['class'];
            $i = new $s();
            if($properties['col']) {
              //dependent table
              $this->data->$field = $i->find($this->data->self->{$properties['col']})->current();
            } else {
              //not dependent table
              $this->data->$field = Models_Loader::get('UserAccount')->find($properties['fk'], $this->id);
            }
          }
        }
      }
    }
  }

  public function find($field, $value) {
    $record = new $this->_map['self']();
    $table = $record->_name;
    $dbObj = Zend_Registry::get('db');
    if(!is_array($value)) {
      $sql = 'SELECT '. self::$pk .' FROM '. $table ." WHERE {$field} = \"{$value}\" LIMIT 1";
      $id = $dbObj->fetchOne($sql);
      if(is_numeric($id)) {
        $this->id = $id;
        $this->build();
        return $this;
      } else {
        return false;
      }
    } else {
      $sql = 'SELECT '. self::$pk .' FROM '. $table ." WHERE {$field} IN (". implode(',',$value) .')';
      return $dbObj->fetchAll($sql);
    }
  }

  public function insert($data) {
    return $this->selfObject->insert($data);
  }

  public function toArray() {
    $d = array();
    foreach($this->data as $i=>$v) {
      if(get_class($v) == 'Zend_Db_Table_Row') {
        $d[$i] = $v->toArray();
      }
    }
    return $d;
  }

}

Anyways, this was my quick solution. As always, looking forward to your feedback!


So my last post I showed a simple little form I had created for letting people create records. Let’s talk about something a little more useful this time. It is very common to want to include a login form, or if they have logged in, some user control bar on every page in your site/application. Let me emphasize, I am by no stretch of the imagination the holy grail of how to do stuff, and would be safe to say that since I am suggesting this approach it is, not the best way. :) However, it has served me well in the few applications I have made with Zend Framework, thus I will share.

** Update **

File Structure
Below is how I structured my files:
+ Project

+ app
  + forms
  + layouts
    + helpers (If you put your view helpers here, register this dir as a view helper dir)
    + scripts
  + lib
  + models
  + modules
    + default
      + controllers
      + views
        + scripts
        + helpers (default location Zend FW looks for View Helpers when default         module invoked)
+ config
+ misc
+ www

Invoking Code
Since, we will be putting this on every page it makes sense to put the invoking code in Zend_Layout. If, you aren’t using Zend_Layout.. I highly suggest you start migrating it into your code. Zend Layout basically allows you to have a “layout” for different pages, which renderes “templates” that correspond to the actions. (That’s a nutshell version) So in my layout I simply put:

echo $this->userControlBar();

This will invoke Zend_View to look for the view helper: UserControlBar. Now it’s good to know a couple of things here, each page request will only have one Zend_View object. So, Zend_Layout, doesn’t have it’s own view object, rather the Zend_View is injected into the Zend_Layout. This means, that the view helper User Control Bar, has to be registered with the view for that action. I may be over-complicating the issue, just be sure that you could call $this->userControlBar() in the template scripts corresponding to all the actions. :)

The View Helper
Now, we need the View Helper. In the helper, we will call default View Helpers that come with Zend_View but in order to do that we have to register the View object with the View Helper. To do that we simply add the following method to our View Helper:

    public function setView($view) {
        $this->_view = $view;
    }

Then the next method we make is the actual View Helper:

    public function userControlBar() { echo 'hi'; }

This should return ‘hi’ in the Layout. Now we simply, need to make it work. We want to first determine if the user has been authenticated which can be done with:

    Zend_Auth::getInstance()->hasIdentity()

This, as expected, will return true or false. So let’s put it in a little if statement:

    if(Zend_Auth::getInstance()->hasIdentity()) {
      //user is logged in
    } else {
      //user is not logged in
    }

Now let’s just say we want 3 controls, and we want the html to be:

We can do this very easily with two default View Helpers: (htmlList, and url). First thing we need to do is build an array of the links we want to show. You -could- abstract this out another level it warranted it, but we have a simple app, so we can just hard code this array (note how we are utilizing the registered view):

   $links = array();
   $links[] = '<a href="' . $this->_view->url(array('controller'=>'action1'),
 '', true) . ">Control One';
   $links[] = '<a href="' . $this->_view->url(array('controller'=>'action2'),
'', true) . ">Control Two';
   $links[] = '<a href="' . $this->_view->url(array('controller'=>'action3',
'param'=>'value'), '', true) . ">Control Three';

Then we can register the $links array with the View Helper htmlList:

   //Should change name most likely :)
   $linkList = $this->_view->htmlList($links, false, array('id'=>'userControl') , false);

The above code will generate the HTML we need. So our ViewHelper now looks like:

   if(Zend_Auth::getInstance()->hasIdentity()) {
      //user is logged in
      $links = array();
      $links[] = '<a href="' . $this->_view->url(array('controller'=>'action1'), '', true)
 . ">Control One';
      $links[] = '<a href="' . $this->_view->url(array('controller'=>'action2'), '', true)
 . ">Control Two';
      $links[] = '<a href="' . $this->_view->url(array('controller'=>'action3',
'param'=>'value'), '', true) . ">Control Three';
      //Should change name most likely :)
      $linkList = $this->_view->htmlList($links, false, array('id'=>'userControl') , false);
      return $linkList;
   } else {
      //user is not logged in
   }

Now that’s all good and dandy, but what if they aren’t logged in? Well, we have two options here, we can just write the Form ourself or use Zend_Form. I personally love Zend_Form. However for sake or this tutorial we will just generate the html ourself. You can simply put the html of the form in a variable and return it for now, that way if you want to do it the right way later, and use Zend_Form, it’s really easy :)

      ...
   } else {
       $form = '... html for login form here ...';
      return $form;
   }

That’s it. Now in all of our pages we will either have a login form or a control bar. We’ve used the View Helpers, Zend_Layout, *hopefully Zend_Form*, wrote our own Helper, and the gorgeous part is we haven’t touched the “Application” code inside the Modules/Defaults/Controllers. We simply listen to Zend_Auth and return whatever.

Hope this helps, and I’d love to see how I could improve my technique.


ZF & Zend_Form

07May08

So I’ve been using ZF more and more, and have come across another reason Zend Framework is just superior imo. Zend Form!

All developers have had the same issues with Forms, and keeping it a) reusable, b) clean, c) contained. Zend_Form does this all. At first it may seem a little confusing, especially if you aren’t familiar with OOP and Decorators, but it’s just another opportunity you get to learn by example. Here is a simple form of mine that lets you add a club, and it’s located in: Project/app/forms/

class forms_AddClub extends Zend_Form {
public function __construct($options = null) {

parent::__construct($options);

$this->setName('register');
$this->setAttrib('enctype','multipart/form-data');

$fields = array();
$empty = new Zend_Validate_NotEmpty();
$empty->setMessage('is required');

$name = new Zend_Form_Element_Text('name');
$name->setLabel('Club Name');
$name->setRequired(true);
$name->addValidator($empty);
$fields[] = $name;

$addy = new Zend_Form_Element_Text('address');
$addy->setLabel('Street Address');
$addy->setRequired(true);
$addy->addValidator($empty);
$fields[] = $addy;

$city = new Zend_Form_Element_Select('cityID');
$city->setLabel('City');
$city->setMultiOptions(array('1'=>'Dallas', '2'=>'Addison'));
$fields[] = $city;

$state = new Zend_Form_Element_Select('stateID');
$state->setLabel('State');
$state->addMultiOption('1','Texas');
$state->setAttrib('disabled', 'disabled');
$fields[] = $state;

$zip = new Zend_Form_Element_Text('zip');
$zip->setLabel('Zip');
$zip->setAttrib('class', 'il');
$zip->setAttrib('size',7);
$zip->setRequired(true);
$zip->addValidator($empty);
$zip->addValidator(new Zend_Validate_Int);
$fields[] = $zip;

$website = new Zend_Form_Element_Text('website');
$website->setLabel('Website');
$website->addValidator($empty);
$fields[] = $website;

$desc = new Zend_Form_Element_Textarea('description');
$desc->setAttrib('cols', 27);
$desc->setAttrib('rows', 6);
$desc->setLabel('About');
$desc->addValidator($empty);
$fields[] = $desc;

$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Add Club!');
$submit->setIgnore(true);
$fields[] = $submit;

$this->addElements($fields);

$this->clearDecorators();
$this->addDecorator('FormElements')
->addDecorator('HtmlTag', array('tag' => '
    ')) ->addDecorator('Form'); $this->setElementDecorators(array( array('ViewHelper'), array('Description'), array('Label', array('separator'=>'')), array('HtmlTag', array('tag' => 'li')) )); // buttons do not need labels $submit->setDecorators(array( array('ViewHelper'), array('Description'),
array('HtmlTag', array('tag' => 'li', 'class'=>'submit-group')),
    )); } }

Then all I do in the controller is $form = new Forms_AddClub(); and I can call methods like $form->isValid(); or just echo $form and I am done! Yeah, it’s nice!


The Zend Framework team conducted a 2hour QA session earlier today, and I was only able to catch the first half of it and here are some of the key parts that I caught wind of I wanted to share with the rest of the world.

Future of ZF
The roadmap of ZF stretches far, far beyond the current location with both an active community & an active core team. With discussions with CMS vendors, JS Frameworks, and intent to integrate ZF into these “unspoken” third party vendor style partnerships not only is ZF growing vertically but also horizontally.

They also are excited about php 5.3 with namespaces, late static binding, and keeping ZF in parallel (to a degree) with the language (php):

We want to be a driving force in the community in shaping the future of the language

Zend Layout
A question was brought on the best practice of having a different layout defined for each module in Zend_Layout, and it was concluded that simply having an abstract class per Module that Controllers extended which defined the layout, and any other module specific controller needs would be the best bet to reduce code duplication.

Command Line / Tooling
A lot was discussed about tooling. And they wanted to let the community know that they believe that Tooling goes beyond Command Line and in keeping the integrity of building from the ground up and easily extending to fit custom needs Tooling should be defined outside of command line such as tooling could also be IDE Driven, or even a web based GUI to change configuration and such. After a bit of discussion they wanted to let the community know proposals were being drafted and a lot of notes & discussions had taken place inside the walls at Zend to think of the best way to do this.

Testing
The Zend Core team really liked a lot Fire_Bug a lot (currently in the Proposals section of the wiki) and will look to build on top of this.

There was also discussion around the new suite based off the popular PHPUnit which will allow the test cases to not only work with css selectors and dom queries (as in popular js frameworks currently) but it also will allow a set of utilities for common test, such as (making sure a db incremented properly and such).

SOAP
Was explained why process on this was low, resource problems and such; and that in the near future (1.6 to 2) there will be WSDL generation, easy PHP class plug & play, logging, debugging, etc. support in the SOAP library.

Active Record
As stated above the team was really excited about 5.3 introducing Late Static Binding as they feel it will allow them to implement a “pure” Active Record.

Zend Framework Certification
They let it out of the bag that there will be a ZF Certification in the future, how near.. well that was yet to be determined. :) That’s about all we know about that at this current time.
That’s about all the notes I could take before I had to bail.. thoughts? Did I miss any key features? Let me know!

** Ryan_Brooks from #zftalk showed me his blog on different topic **


So I have had the DISTINCT pleasure of implementing a “new & innovative” navigational control from the brilliant minds of our Creative department at work. Let me lay down a little bit of the ground work of how this surfaced:

Creative doesn’t want navigation to fall below the fold. They don’t want there to be any natural scrolling on the site. Apparently the built in scroll bars in all the browsers just aren’t “creative” enough for there taste. They also want to represent the hiearchy of the sub navigation via a tree structure (hence the Tree). On top of that, they don’t want to confuse the user with showing anymore than one element at a time. So when one parent is invoked all other expanded parents are collapsed(hence the Accodian).

If this wasn’t brilliant enough, remember the first concern.. don’t want to see a scroll bar even though we are jamming 100 links in 4 sections in the sub navigation. So, the last requirement is if one of the “panes” in the accordian section is larger than X height we want to allow them to scroll within the pane.

Which leaves us with a Scrollable TreeCordian which I wrote using MooTools 1.21b.

Check Out My Example, & Feel Free to Review Code w/ Ways to Improve!


MooTools

29Apr08

So I’ve been doing a lot with MooTools JavaScript library for about 10 months now. From beta to now 1.21b. And, it is my favorite framework of choice. So, I will be building a collection of components and Moo plugins for the world to use to share my love and gee!

Look for them to come soon!