Breerly

Tidbits for web development.

Hello World

Hey there world. This post is coming from Octopress, a responsive blogging framework for hackers. I’ve decided to check it out. No platform, no site. Just code. Github & I. Stay tuned for neato posts that will boggle your mom’s brain.

Using AjaxContext With Zend_Rest_Controller & Zend_Rest_Route

If you haven’t gotten a hang of setting up Zend_Rest_Route’s with Zend_Config_Ini check out our first post here.

Not only does using REST in Zend Framework make access to your common resources uniform and simple, they also can do the same for your front end. Instead of making new actions for every ajax request, simply reuse the same actions your REST controllers already have. Need to get a json representation of all resources of a certain type (say articles), then re-use ArticlesController::indexAction. Need to get just one? ArticlesController::getAction is your man. Create, update, or delete? PostAction, putAction, and deleteAction, respectively. Using the AjaxContext action helper, this is made possible in only a few lines. The example below uses Articles as the example resource at hand, and shows you how to interact with every action of your REST controller from your JavaScript front-end. We use Dojo to streamline the different type of AJAX calls but it should be fairly easy switching these methods out with those from your favorite library.

1
2
3
;routes.ini
routes.rest.type = Zend_Rest_Route
routes.rest.default = articles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php /* ArticlesController.php */

ArticlesController extends Zend_Rest_Controller
{
  // ajax context for get, post, & put
  public function init()
  {
    /* @var $ajaxContext Zend_Controller_Action_Helper_AjaxContext */
    $ajaxContext = $this->_getHelper('AjaxContext');
    $ajaxContext->addActionContext('index', 'json');
    $ajaxContext->addActionContext('get', 'html');
    $ajaxContext->addActionContext('post', 'json');
    $ajaxContext->addActionContext('put', 'json');
    $ajaxContext->addActionContext('delete', 'json');
    $ajaxContext->initContext();
  }
  public function indexAction() {
    $this->view->articles = array(
      array('id' => 1003, 'title' => 'Article 1'),
      array('id' => 1004, 'title' => 'Article 2')
    );
  }
  public function getAction() {
    $id = $this->_getParam('id');
    $this->view->id = $id;
  }
  public function postAction() {
     $id = $this->_getParam('id');
     $this->view->response = array(
        'message' => $id . ' created successfully'
     );
  }
  public function putAction() {
     $id = $this->_getParam('id');
     $this->view->response = array(
       'message' => $id . ' updated successfully'
     );
  }
  public function deleteAction() {
     $id = $this->_getParam('id');
     $this->view->response = array(
       'message' => $id . ' deleted successfully'
     );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php /* index.phtml */ ?>

<a id="testIndex">AJAX INDEX</a>
<a id="testGet">AJAX GET</a>
<a id="testPost">AJAX POST</a>
<a id="testPut">AJAX PUT</a>
<a id="testDelete">AJAX DELETE</a>

<script type="text/javascript">

//AJAX INDEX
dojo.query('a#testIndex').onclick(function(event){
    dojo.xhrGet({
        url : '/articles/format/html',
        load : function(response){
            console.log(response);
        }
    });
});

//AJAX GET
dojo.query('a#testGet').onclick(function(event){
    dojo.xhrGet({
        url : '/articles/1005/format/html',
        load : function(response){
            console.log(response);
        }
    });
});

//AJAX POST
dojo.query('a#testPost').onclick(function(event){
    dojo.xhrPost({
        url : '/articles/format/json',
        handleAs : 'json',
        content : { 'title' => 'New Article' },
        load : function(response){
           console.log(response);
        }
    });
});

//AJAX PUT
dojo.query('a#testPut').onclick(function(event){
    dojo.xhrPut({
        url : '/articles/1005/format/json',
        handleAs : 'json',
        content : { 'title' : 'Updated Article' },
        load : function(response){
           console.log(response);
        }
    });
});

//AJAX DELETE
dojo.query('a#testPut').onclick(function(event){
    dojo.xhrDelete({
        url : '/articles/1005/format/json',
        handleAs : 'json',
        load : function(response){
           console.log(response);
        }
    });
});

</script>
1
2
<?php /* get.ajax.phtml */ ?>
Ajax Response for Article <?php echo $this->id ?>

RESTful Routes With Zend_Rest_Route & Zend_Config_Ini

Lets say you have a thing in your system, a resource rather. That thing is posts. These are the things you need to do with your system’s posts.

  • You need to list all posts.
  • You need to list a specific post.
  • You need to create new posts.
  • You need to update a post.
  • You need to delete a post.

Now replace posts with private messages, tweets, songs, videos, or articles. What you’ll see is that the basic requirements for all these things are the same. You need to be able to perform CRUD on all of them. The amount of times this pattern works is just astonishing. And Zend Framework makes solving it trivial. Below is an end to end solution for rigging up Zend_Rest_Route with a Zend_Rest_Controller using Zend_Config_Ini. Enjoy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    /**
     * add routes configuration to the router
     */
    public function __initRouteConfig()
    {
        $this->bootstrap('frontController');
        $frontController = Zend_Controller_Front::getInstance();
        $router = $frontController->getRouter();
        $config = new Zend_Config_Ini(
            realpath(APPLICATION_PATH . '/configs/routes.ini'),
            APPLICATION_ENV));
        $router->addConfig($config, 'routes');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[production]

; simple rest route for the
; default module. index, account, and users controllers
; support module. export and create controllers
routes.rest.type = Zend_Rest_Route
routes.rest.default = index,account,users
routes.rest.support = export,create

; rest route at the end of a chain
routes.admin.route = "/administration"
routes.admin.chains.rest.type = Zend_Rest_Route
routes.admin.chains.rest.admin = manage,view

[testing : production]
[development : production]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

class UsersController extends Zend_Rest_Controller
{
    public function indexAction()
    {
        // list all users...
        // GET to /users
    }
    public function getAction()
    {
        // list one user...
        // GET to /users/1095
        $id = $this->_getParam('id'); //contains 1095
    }
    public function postAction()
    {
        // create new user...
        // POST to /users
    }
    public function putAction()
    {
        // update existing user...
        // POST to /users/1095?_method=PUT
        $id = $this->_getParam('id'); //contains 1095
    }
    public function deleteAction()
    {
        // delete existing user...
        // POST to /users/1095?_method=DELETE
    }
}

5 Online Comics Developers Should RSS

Webcomics are great. Well, some are. Most are too long, posted too often, and not too funny. A surprising amount of online comics don’t even embed images in their feeds. I’ve rounded up a list of quality, light, and readable webcomics that will stand the test of time in any developer’s feed reader.

Utopia Theory

Our absolute favorite. Utopia Theory’s strips are short and sweet, making it a no-lose comic to RSS. The bits often include popular pop culture characters like Spongebob, Ms Pacman, Mr. Potato Head, Aladdin, and Tony the Tiger in unfortunate real life situations. Do yourself a service and nab this Farside-esque feed. Get the RSS Here.

Abstruse Goose

Abtruse Goose is blog any intellectual with a sense of humor will enjoy. Linux, string theory, pi, physics, math, engineering, computers, and other geekery meets every day scenarios. Enjoy this feed over your next build or code compilation.

Calvin and Hobbes

Aaah the simpler days. Those who remember Calvin and Hobbes might be surprised to find out it’s available on the web. Little introduction is needed for the the little boy and his stuffed tiger. Unfortunately, their feed doesn’t embed the images. We’ve got a secret though; the site and their feed is ridden with ads but this RSS feed is sparkling clean.

Crimes Against Hugh’s Manitees

Crimes Against Hugh’s Manitees is an obscure webcomic which features animals with personality disorders. Very light and humorous. Looks like the comics are made by hand with construction paper, a felt tip, and glue. We keep these little guys around for the awkward honesty and clever anthropomorphism. Furry feed here.

Installing Puppet on EC2’s Amazon Linux AMI

Amazon Web Services only recently released their Amazon Linux AMI; a stable, secure, rhel-flavored, and performant Linux environment for applications running on EC2. The AMI is free, automatically updated, and EBS or S3 backed. It also comes with Ruby pre-installed. This makes it a killer platform for installing Puppet, an open source data center automation and configuration management framework. I’ve pulled together a slimmed down installation guide for those looking for the “just works” version.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# RDEVEL, IRB, RDOC, RUBYGEMS
sudo yum install ruby-devel ruby-irb ruby-rdoc rubygems

# FACTER
wget http://puppetlabs.com/downloads/facter/facter-latest.tgz
tar -xf facter-latest.tgz
cd facter-*
sudo ruby install.rb

# PUPPET
wget http://puppetlabs.com/downloads/puppet/puppet-latest.tgz
tar -xf puppet-latest.tgz
cd puppet-*
sudo ruby install.rb

ruby --version
# ruby 1.8.7 (2010-08-16 patchlevel 302) [x86_64-linux]

rdoc --version
# RDoc V1.0.1 - 20041108

irb --version
# irb 0.9.5(05/04/13)

facter --version
# 1.5.8

puppet --version
# 2.6.4

Once Puppet is installed, you should configure it. We will be releasing a follow up that will significantly slim the work involved soon. But until then, check out PuppetLab’s Configuration Guide.

Getting a Row Count With Zend_Db

It really surprises me when something as common as getting a row count is rather obscure in the Zend Framework world. I’ve seen plenty of implementations but none of them are light and straight-forward. Here are two implementations, procedural & OO, for the developer who is as perplexed as I was. The first example is procedural and should give you the most flexibility on how and where you put it.

1
2
3
4
5
6
7
<?php

// getting the count in a procedural script
// tip: use an indexed column instead of *, like id
$adapter = Zend_Db_Table::getDefaultAdapter();
$select = $adapter->select()->from('table', 'COUNT(*)');
$count = (int) $adapter->fetchOne($select);

The second assumes you have a Zend_Db_Table class defined and good seperation using a mapper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

/**
 * it's best to use a indexed column like id
 * if you don't have an indexed column you can use COUNT(*)
 * but it is less performant in larger InnoDB tables
 *
 * @example echo count(new Application_Model_Mapper_MyTable);
 */
class Application_Model_Mapper_MyTable
  implements Countable
{
    protected $_table;
    public function __contruct()
    {
        $this->_table = new Application_Model_DbTable_MyTable();
    }
    public function count()
    {
        return (int) $this->_table->getAdapter()->fetchOne(
            $this->_table->select()->from($this->_table, 'COUNT(id)')
        );
    }
}

5 SaaS Friendly Services You Should Know About

Startups as well as established business can both benefit from online services that streamline their needs and reduce maintenance. Below is a list of 5 helpful services that do just that.

Beanstalk Private Hosted Version Control

Every startup should have some sort of version control. The laundry list for setting up and maintaining such a system isn’t short: setup the repository on a box available to all developers, make sure it can handle the load being put on it, make sure there is a backup mechanism… Not anymore. Beanstalk offers private and secure Subversion or Git repo hosting. It’s really beautiful because they’ve really created a worry-free, reliable service with frequent multi-site backups, SSL, scalable servers, and an API to boot. Once we made the dive we haven’t turned back. They offer a free plan so it’s a definite yes.

Hudson as a Service

We like Hudson, it’s a great continuous integration system. It has a rich and powerful user community, a short release cycle, and hoards of plugins that make it support building virtually any combination of technologies. Most developers recognize the importance and benefits of continuous integration - whether your running unit tests after every commit, pushing to a shared environment, or orchestrating entire releases; CloudBees has you covered. The biggest pain in the butt we’ve come to know is that Hudson or any other CI solution (xinc, CruiseControl, etc…) is just a monster pain to setup and keep healthy. With CloudBees HaaS it’s all done for you; and you just pay for usage. Never before has it been truly possible to run a full software team on just SaaS services - CloudBees is pushing us through the gap. Tip* - run a post commit hook from your beanstalk account to a Hudson job to get quick and easy CI going.

Zerigo Managed DNS Hosting with API

When it comes to DNS, you can’t play around. Zerigo offers low 1 minute TTL and API access. The UI is sweet and straightforward and there is a free plan to boot. When it comes to other offers, Zerigo is a no-brainer. For us the most important thing has been resolution time, wildcard hostnames, and usage based payments. They do it all. I’m sorry ZoneEdit, but it was good while it lasted.

Chargify Recurring Billing API

Most SaaS services have the goal of offering the entire scope of their service in one place online. Billing is a big part of this. Chargify makes it trivial to setup and maintain subscription billing through the use of their API and hosted payment pages. We love Chargify and are amazed at the amount of use cases they solve. There isn’t a quicker way to get a PCI compliant, metered or subscription billing solution with support for coupons/discounts and easy billing analytics. Chargify also support all major payment gateways. When it comes to billing, there is no reason to remake the wheel; in fact, before we started using Chargify, we had no idea what a dunning process was.

Google Apps for Business

Need company email, docs, spreadsheets, calendars, and all the IT systems you’ve come to expect while working at any medium or large company? We did. And we didn’t want to setup and maintain an exchange server, or pay microsoft licensing on a per box and year bases. Google Apps is a godsend to any company who want’s to rid the extra weight and mind share. Simply sign up and go; it’s $50 per person a year. We even use Google Apps to send emails to our users through our application. We really couldn’t go back now.

Zend_Db Configs Rounded Up for Oracle, SQL Server, PostgreSQL, & MySQL.

Here’s a quick reference for configuring Zend Framework with the most popular databases. Beats me how this never made it into the manual.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
; DB ADAPTER MYSQL
resources.db.adapter = "pdo_mysql"
resources.db.params.dbname = "database"
resources.db.params.username = "user"
resources.db.params.password = "pass"
resources.db.isDefaultTableAdapter = true
;the following is required by some machine configurations
resources.db.params.unix_socket = "/var/lib/mysql/mysql.sock"

; DB ADAPTER ORACLE
resources.db.adapter = "oracle"
resources.db.params.dbname = "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.1)(PORT = 1521)))(CONNECT_DATA = (SID = database)))"
resources.db.params.username = "user"
resources.db.params.password = "pass"
resources.db.isDefaultTableAdapter = true

; DB ADAPTER POSTGRESQL
resources.db.adapter = "pdo_pgsql"
resources.db.params.host = "localhost"
resources.db.params.username = "user"
resources.db.params.password = "pass"
resources.db.params.dbname = "database"
resources.db.isDefaultTableAdapter = true

; DB ADAPTER SQL SERVER
resources.db.adapter = "sqlsrv"
resources.db.params.host = "CCVMXP-TH36BSOI"
resources.db.params.username = "user"
resources.db.params.password = "pass"
resources.db.params.dbname = "database"
resources.db.isDefaultTableAdapter = true

Multiple File Upload With HTML5 & Zend_Form

Here’s a nifty trick for enabling multiple file uploads through one input element with HTML5 and Zend_Form. Developers familiar with Zend Framework’s form component should be delighted with the brevity of this solution.

1
2
3
4
5
<?php

$element = new Zend_Form_Element_File('multifile');
$element->setAttrib('multiple', true);
$element->setIsArray(true);