So, I’ve started the new gig at Splash, a social media company, and I got to talk about my POV on Social & Search.  Check it out:


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.




Follow

Get every new post delivered to your Inbox.