Cory Wiles Blog

If You Blog It They Will Come 
Filed under

zend framework

 

Using Zend View for Email Message Body in Your Model

Lately in the PHP/Zend Framework blog world there has been much discussion concerning what constitutes a model in an MVC app.  In the current implementation of the MVC apps at work our ZF Form implementations are processed in a corresponding model class as well as a 'notify' method which handles emailing a response after successful submission.  I was able to abstract all aspects of the email properties except for the message body.

Most of the time the requirement is to have, basically, the same form view but with the values populate.  That usually means creating some long and kludgy looking heredoc or worse a huge string of crazy html intermingled with escapes.  Neither solution was very appealing to me.  I kept thinking more and more that the email body was really a view.  So I treated as such.  So my solution was to create a global views directory and a view script that was the email body template and passed it's render method to Zend_Mail instance.

This allows for further separation of models and views.  I know that technically the email body should be apart of the model, but far too many times I have to change how the body of the mail looks, not the data, so it lends itself to more of a view.

Please feel free to comment.

::NOTE::
Forms as model architecture taken from http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html

//Example Code
private function _notifyGroup() {
 
  $data = array();
  $data = $this->getForm()->getValues();
 
  /**
   *  Setup view instance to pass to notifier
   */
  $view = new Zend_View();

  /**
   * Assign all form data to view property
   */
  $view->assign("formvalues", $data);

 
 /**
   * Location of view scripts
   */
  $view->addScriptPath(dirname(__FILE__).'/../../../../views');

  $body = $view->render('email-templates/mir.phtml');

  /**
   *  Custom notify class that abstracts different 'notify' methodologies.  For example you can notfiy
   *  by writing to a log or email.  Email can be Zend_Mail, PEAR or plain ol' php mail() 
   */
  $notifier = CW_Notify::factory('mail_zend', $body, $options['email']);

  $notifier->notify();

}

Loading mentions Retweet
Filed under  //   views   zend   zendframework  

Comments [0]

PayPal Zend Framework Validator

I was presented with a project where the client wanted a form that would accept credit card payments via paypal.  I could either write it in PHP or Java. While I am a big fan of both languages I felt in this instance that PHP was the most time efficient route.   However, PayPal doesn't offer an API for PHP. Only Java and .NET.  As to not be discouraged I decided to spend a little time trying to come with a custom validator that would handle the validation of the credit card validation.

::Warning::
My use case was very specific.  I only needed to authorize credit card sales.  Basically their simpliest form of transations.

Since all the applications that I write now are us the Zend Framework I was able to reduce "inline-code" by creating a custom validator that I assign to my preValidation method inside of the model.  :: I worked on this concept with Jeremy Kendall ::

There is definitely room for improvement, but this is might be helpful to others.  For example, the paypal options should really not be declared as Zend_Config instance, but checked for an instance of Zend_config and then set as an array with the toArray method.


// Validator

/**
* @see Zend_Validate_Abstract
*/
require_once 'Zend/Validate/Abstract.php';

/**
* @see Zend_Http_Client
*/
require_once 'Zend/Http/Client.php';

class SJCRH_Validate_PayFlow extends Zend_Validate_Regex {

const CHARGE_FAILED = "ppChargeFailed";

const CHARGE_EXCEPTED_CODE = "0";

/**
* Error message to display to the user if the credit card transaction fails
*
* @access protected
* @var array
*/
protected $_messageTemplates = array(
self::CHARGE_FAILED => "There was an error processing your card. Error code: '%code%' Error message: '%msg%'"
);

/**
* @var array
*/
protected $_messageVariables = array(
'code' => '_code',
'msg' => '_msg'
);

/**
* Error Code value
*
* @var mixed
*/
protected $_code;

/**
* Error Message
*
* @param Zend_Config $options
*/
protected $_msg;


/**
* CC validation using PayFlow
*
* @param Zend_Config $options
* @param string $ccnum
* @param string $exp
* @param array $billing
* @param string $amount
* @param string $country
*/
public function __construct(Zend_Config $paypaloptions, $ccnum, $amount, $exp, $billing = array(), $country = 'US') {

if (!$paypaloptions instanceof Zend_Config) {
throw new Exception("Options must be an instance of Zend Config");
} else {
$options = $paypaloptions->toArray();
}

/**
* Random string used to error checking with payflow - DUPLICATION TRANSACTIONS
*/
$requestId = md5(date('YmdGis'));

$zfClient = new Zend_Http_Client($options['url']);

$zfClient->setHeaders(array('X-VPS-Request-ID' => $requestId));

/**
* Recommended from their documentation to change the timeout from 30 seconds (default)
* to 45
*/
$zfClient->setConfig(array('timeout' => 45));

$zfClient->setMethod(Zend_Http_Client::POST);

$zfClient->setParameterPost(array(
'USER' => $options['username'],
'VENDOR' => $options['vendor'],
'PARTNER' => $options['partner'],
'PWD' => $options['password'],
'FIRSTNAME' => $billing['firstname'],
'LASTNAME' => $billing['lastname'],
'STREET' => $billing['street'],
'ZIP' => $billing['zip'],
'TENDER' => 'C',
'TRXTYPE' => 'S',
'ACCT' => $ccnum,
'EXPDATE' => $exp,
'AMT' => $amount,
'CURRENCY' => 'USD',
'COUNTRY' => $country,
'CLIENTIP' => $_SERVER['REMOTE_ADDR'],
'VERBOSITY' => 'MEDIUM'
));

/**
* The response key/value pairs are seperated by ampersands.
*
* I extract the RESULT and RESPMSG
*
* If anything but a RESULT=0 is found then the code and error message are
* set to the validator's variables to display to the form user.
*/

$payFlowResponse = explode("&", $zfClient->request()->getBody());
$regex = "/(\w*)=(.*)/i";

@preg_match($regex, $payFlowResponse[0], $matches);

$this->setCode($matches[2]);

if ($this->getCode() !== self::CHARGE_EXCEPTED_CODE) {
@preg_match($regex, $payFlowResponse[2], $matches);
$this->setMsg($matches[2]);
}
}

public function isValid($value) {

$this->_setValue($value);

if ($this->getCode() !== self::CHARGE_EXCEPTED_CODE) {
$this->_error();
return false;
}

return true;
}

public function setCode($code) {
$this->_code = $code;
}

public function setMsg($msg) {
$this->_msg = $msg;
}

public function getCode() {
return $this->_code;
}

public function getMsg() {
return $this->_msg;
}
}

Loading mentions Retweet
Filed under  //   paypal   validator   zend   zendframework  

Comments [0]

Internalization and Zend Form

One of the many growing requirements that I am experiencing at work is for internalization of our forms.  In this particular use case the languages available are limited to English and Spanish so I am not able to use Zend_Locale::BROWSER exclusively.  To make sure that correct form displays, in my form model class I created an array of allowed locales.  Before the form instantiated in the controller the language param is set the registry, and then that locale is checked against allowed locales.

References:

Translations - located in my models directory: Models/Languages/En.php and Es.php

// En.php

return array(
              'name'    => 'First Name',

              'address' => 'Address'
       );

// Es.php

return array (

         'name'     => 'nombre',         'address' => 'dirección'

       );


// Controller snippet
 /**
  * Add language to the registry
  */
  $locale = new Zend_Locale($this->_getParam('lang'));
  Zend_Registry::set('locale', $locale);

// Form snippet
  /**
   * Instance of Zend_Locale
   *
   * @var Zend_Locale
   */
  protected $_locale         = null;

  /**
   * Allowed locale regions
   *
   * @var array
   */
  protected $_allowedLocales = array('en', 'es');

  protected $_translations   = null;

    $this->addElement('note', 'quanityInstructions', array(
      'decorators'  => $this->_standardNoteDecorator,
      'description' => "{$this->_translations->_('quanityInstructions')}"
    ));

  /**
   * Prepare the form
   *
   * @return boolean
   */
  private function _prepare() {

    /**
     * Grab the browsers locale
     */
    $this->_locale = Zend_Registry::get('locale');

    /**
     * If the browser is locale isn't English or Spanish then default to
     * English
     */
    if (!$this->_checkForValidLocaleRegion($this->_locale)) {
      $this->_locale = new Zend_Locale('en_US');
    }

    $transFile = ucfirst($this->_locale->getLanguage()).'.php';

    $this->_translations = new Zend_Translate('array', realpath(dirname(__FILE__).'/../../Path/To/Languages/'.$transFile), $this->_locale);

    return true;
  }

  /**
   * Check to make sure that browser's locale is english or spanish.  The region
   * doesn't matter.
   *
   * @param Zend_Locale $zl
   * @return boolean
   */
  private function _checkForValidLocaleRegion(Zend_Locale $zl) {
    return in_array($zl->getLanguage(), $this->_allowedLocales) ? true : false;
  }

If locale is allowed then the form label translation is set.

Loading mentions Retweet
Filed under  //   internalization   zend   zend form   zendframework  

Comments [0]

Zend Framework Dynamic Site - In Production

Back in April I wrote a blog post discussing my concept of having a dynamic site using the Zend Framework.  In addition, I posted an some example code of how everything works.  I am a firm believer that one should practice what they preach and two different sites are now in production using the framework that I wrote and so far it works beautifully.  I had to make a few changes to the route to allow for module exceptions.

Towards the end of the project there was a request to have a search functionality and also custom forms.  Normally this wouldn't be an issue what so ever, but the way that the custom route is setup all requests are send to the default module/index controller/index action.  I modified the route to ignore any request that started with search or forms and route those requests to the search or forms module.  The regex is easily modified to allow for other exceptions.

Custom Regex:
<route>^(?!\bsearch|forms\b)(\w+-?[\w-?]*\/?)(.*)?</route>

I also setup the ability to add in meta keywords and meta description tags in the content.xml file.  Finally, both sites use the EXACT same doc root and dynamic site framework.  Since both sites use the same layouts, just different menus and different background images, I didn't want to duplicate a lot of code.  So in the setup page display plugin I am able to transverse the content mapping file based upon not only the request, but the url host name to display the proper layout.

Loading mentions Retweet
Filed under  //   custom   dynamic   framework   regex   sites   zend framework  

Comments [0]

Automate Adding Twitter Followers with Zend Framework

Adding followers to a user one at a time is sometimes a VERY lengthy process.  Last night the question was posed to me if there was a way to automate adding followers from users who follow someone else.

Example scenario:
John Doe follows Jane Doe
John Doe wants to follow at 600 followers of Jane Doe

Unfortunately, the Zend_Service_Twitter class doesn't offer any functionality to retrieve a list of followers from another user, but I was able to extend the class and add the functionality.

The particular user that I chose to test has roughly 6,000 users.  My client didn't want all 6,000, but the first 200.  Within 10 minutes the new custom class was written,  the script ran, and now my client was now following 200 new people of like mind. :)

I plan on submitting the feature request to the Zend Framework gurus to have the ability to find followers from other users so that it can be apart of the main service.

Until then here is the class and script:

require_once 'Zend/Service/Twitter.php';

class CW_Twitter extends Zend_Service_Twitter {

  public function __construct($username, $password) {
    parent::__construct($username, $password);
  }

  public function getOtherUserFriends($name) {
        $this->_init();
        $path = '/followers/ids/' . $name . '.json';
        $response = $this->restGet($path);
        return $response->getBody();
  }

}

$twitter = new CW_Twitter('username', 'password');
$response = $twitter->getOtherUserFriends('friendusername');

$arrayObject = new ArrayObject(Zend_Json::decode($response));
$limitIterator = new LimitIterator($arrayObject->getIterator(), 0, 200);

    foreach ($limitIterator as $value):
      $twitter->friendship->create($limitIterator->current());
    endforeach;

Loading mentions Retweet
Filed under  //   apis   automation   custom apis   twitter   zend framework  

Comments [0]