<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jeremy Cook &#187; Zend Framework</title>
	<atom:link href="http://jeremycook.ca/tag/zend-framework/feed/" rel="self" type="application/rss+xml" />
	<link>http://jeremycook.ca</link>
	<description>Random musings on web development and PHP</description>
	<lastBuildDate>Mon, 30 Jan 2012 02:31:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Zend_Form and Validating Values for Multiple Column DB Keys</title>
		<link>http://jeremycook.ca/2011/10/25/zend_form-and-validating-values-for-multiple-column-db-keys/</link>
		<comments>http://jeremycook.ca/2011/10/25/zend_form-and-validating-values-for-multiple-column-db-keys/#comments</comments>
		<pubDate>Wed, 26 Oct 2011 01:11:12 +0000</pubDate>
		<dc:creator>Jeremy Cook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://jeremycook.ca/?p=219</guid>
		<description><![CDATA[I&#8217;ve recently started developing my first project fully in Zend Framework and I&#8217;ve been loving it so far. There are so many components in the framework that make a developers life easier, among which is the combination of Zend_Filter, Zend_Validate and Zend_Form. I love how easy it is to create and validate forms using these [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently started developing my first project fully in Zend Framework and I&#8217;ve been loving it so far. There are so many components in the framework that make a developers life easier, among which is the combination of Zend_Filter, Zend_Validate and Zend_Form. I love how easy it is to create and validate forms using these components, although Zend Form decorators are still a black art to me. Today I came across a particular problem in validating values that I thought I would write about here.</p>
<p>Zend_Validate includes two classes to work with database columns: Zend_Validate_Db_NoRecordExists and  Zend_Validate_Db_RecordExists. As their names suggest these check that a value either does or doesn&#8217;t exist in a specified column in a database table. The classes work really well in a form when specified as a validator for a value but things can get a little trickier if you need to check two columns in a table. The problem I had was in creating a simple FAQ page for a site. The site owner gets to add FAQs to the site and each FAQ is contained in a category. As a little sanity check I wanted to make sure that the combination of category and question was unique. The database also has a multiple column unique key on these to enforce the integrity. My first attempt looked something like this in a class that extends Zend_Form:</p>
<p>&nbsp;</p>
<pre class="brush: php; title: ; notranslate">

public function init() {
//Category options are populated elsewhere.
$categories = new Zend_Form_Element_Select('category');
$categories-&gt;setAllowEmpty(FALSE)
	   -&gt;setAttrib('required', 'required')
	   -&gt;setLabel('FAQ Category:')
	   -&gt;addErrorMessage('Please choose a valid category from the list.')
	   -&gt;addMultiOption('', '--Please Choose--');
$question = new Zend_Form_Element_Text('question');
$question-&gt;setRequired(TRUE)
	 -&gt;setLabel('Question:')
	 -&gt;setAttrib('required', 'required')
	 -&gt;setAttrib('max', '100')
	 -&gt;addValidator('StringLength', FALSE, array(0,100))
	 -&gt;addErrorMessage('Enter a question of up to 100 characters in length.');
$validate = new Zend_Validate_Db_NoRecordExists(array('table' =&gt; 'faqs', 'field' =&gt; 'title'));
$validate-&gt;getSelect()-&gt;where('FaqCategoryId = ?', $this-&gt;getValue('category'), Zend_Db::PARAM_INT);
$question-&gt;addValidator($validate);
}
</pre>
<p>I thought that this would add the extra where clause to the select statement for the validation. This failed miserably though. The problem is that the value referenced in &#8216;$this-&gt;getValue(&#8216;category&#8217;)&#8217;  doesn&#8217;t exist when the init() method is called. Values are only added to the form when the isValid() method is called. The solution was to extend the isValid() method and to perform the extra check there.</p>
<pre class="brush: php; title: ; notranslate">
public function isValid($data) {
	$result = parent::isValid($data);
	if ($result) {
		$validate = new Zend_Validate_Db_NoRecordExists(array('table' =&gt; 'faqs', 'field' =&gt; 'question'));
		//Add a where clause to check the FaqCategoryId
	        $validate-&gt;getSelect()-&gt;where('FaqCategoryId = ?', $this-&gt;getValue('category'), Zend_Db::PARAM_INT);
	       	$result = $validate-&gt;isValid($this-&gt;getValue('question'));
	       	if (! $result)
	       		$this-&gt;getElement('question')-&gt;setErrors(array('The selected category already has an FAQ with that question. Please edit the question.'));
		}
		return $result;
	}
</pre>
<p>By extending the isValid() method I can call the parent method to do the majority of the work. If the form looks valid I then perform the extra check to make sure the combination of question and category is unique. If it isn&#8217;t an error message is set and the validation fails. I&#8217;ve also used this in another part of the site where the client is adding news articles to the site. In that instance the combination of published date and title needs to be unique as these are used in the url to uniquely reference the article. The same method in form validation checks that when the form is submitted. Using this method I&#8217;ve been able to clean up my controller and move all validation checks into the form, where they should be. I&#8217;d love to hear in the comments how other people approach this problem though.</p>
]]></content:encoded>
			<wfw:commentRss>http://jeremycook.ca/2011/10/25/zend_form-and-validating-values-for-multiple-column-db-keys/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using Zend_Service_Twitter and OAuth</title>
		<link>http://jeremycook.ca/2010/08/27/using-zend_service_twitter-and-oauth/</link>
		<comments>http://jeremycook.ca/2010/08/27/using-zend_service_twitter-and-oauth/#comments</comments>
		<pubDate>Fri, 27 Aug 2010 21:14:03 +0000</pubDate>
		<dc:creator>Jeremy Cook</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://jeremycook.ca/?p=153</guid>
		<description><![CDATA[I&#8217;ve just spent all afternoon trying to update a very simple Twitter client to post status updates to Twitter. The code has been using BASIC authentication to post Tweets until now but seeing as that&#8217;s about to be discontinued in favour of OAuth I thought I&#8217;d better update things. I had no problem using Zend_Oauth_Consumer [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just spent all afternoon trying to update a very simple Twitter client to post status updates to Twitter. The code has been using BASIC authentication to post Tweets until now but seeing as that&#8217;s about to be discontinued in favour of OAuth I thought I&#8217;d better update things. I had no problem using Zend_Oauth_Consumer to get an access token and an access token secret but the problems started when trying to use Zend_Service_Twitter.</p>
<p>The first problem came because the online version of the Framework documentation hasn&#8217;t been updated with the latest release. As Zend_Service_Twitter has been largely refactored to use OAuth this is a major issue (the constructor even accepts different arguments from what the documentation says). At no point is it clear what arguments need to be passed to the constructor or in what order. Anyway, after hours of frustration I finally came by some help on StackOverflow. Here&#8217;s some very simple test code to post a Tweet using Zend_Service_Twitter and OAuth (assuming you&#8217;ve already registered an application with Twitter and got the correct user authorisation keys):</p>
<pre class="brush: php; title: ; notranslate">

$access = new Zend_Oauth_Token_Access();
 $access-&gt;setToken('USER AUTHORISATION TOKEN')-&gt;setTokenSecret('USER AUTHORISATION TOKEN SECRET');
 $params = array(
 'accessToken' =&gt; $access,
 'consumerKey' =&gt; 'YOUR APPLICATIONS CONSUMER KEY',
 'consumerSecret' =&gt; 'YOUR APPLICATIONS CONSUMER SECRET'
 );
 $twitter = new Zend_Service_Twitter($params);
 $twitter-&gt;statusUpdate('Testing posting tweets using OAuth!');
</pre>
<p>The statusUpdate method returns an instance of Zend_Rest_Client_Result, which can be checked to see if the request was successful. The method also throws Zend_Http_Client_Exception and Zend_Service_Twitter_exception, which I would of course catch in production code.</p>
<p>&lt;rant&gt;This has taken me hours to figure out. If only the documentation had been properly updated this should have been a cinch to put together. I am a newb at Zend Framework but I think this would have stumped anyone. Please, please, please get this documentation sorted out!&lt;/rant&gt;</p>
<p>Anyway, I hope this helps someone and stops them from facing the hours of frustration I just have.</p>
]]></content:encoded>
			<wfw:commentRss>http://jeremycook.ca/2010/08/27/using-zend_service_twitter-and-oauth/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Creating Professional Documents the Easy Way</title>
		<link>http://jeremycook.ca/2010/05/16/creating-professional-documents-the-easy-way/</link>
		<comments>http://jeremycook.ca/2010/05/16/creating-professional-documents-the-easy-way/#comments</comments>
		<pubDate>Mon, 17 May 2010 02:46:58 +0000</pubDate>
		<dc:creator>Jeremy Cook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Services]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://jeremycook.ca/?p=88</guid>
		<description><![CDATA[Have you ever tried to create a professional quality document programmatically from within PHP? It&#8217;s a real pain and surprisingly tricky to do. There are limited options for PHP developers to produce files in the formats most often used in business. There are libraries for producing PDF files in PHP but they are complex and [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever tried to create a professional quality document programmatically from within PHP? It&#8217;s a real pain and surprisingly tricky to do. There are limited options for PHP developers to produce files in the formats most often used in business. There are libraries for producing PDF files in PHP but they are complex and are difficult to use for producing documents. The options to create Microsoft Word files seem to be even more limited. It is possible to create RTF files from PHP but as these are not the most commonly used format in business the user will have to convert the file to another format before saving it. I&#8217;m working on a project where one of the requirements is for a user to download a a professional quality document with variable content, which is determined by their answers to a questionnaire. I was looking at having to create RTF files before I stumbled on a great service offered by <a href="http://www.livedocx.com/">Live Docx</a> that seems to offer the holy grail of programmatically creating professional quality documents with minimum hassle.</p>
<h2>What is LiveDocx?</h2>
<p>The Live Docx service allows a programmer to take a document template and through a SOAP based web service populate it with content. The resulting document can then be retrieved to the web server in one of many common formats including PDF, MS Word .doc and .docx and RTF. Best of all the service is completely free at the lowest level, but depending on the requirements of your application it may be more appropriate to use one of the paid for levels of service. The templates can be created in .doc, .docx, .rtf and .txd formats and use mail merge to populate the content. The great advantage is that you can use all of the features of a word processing program to design your document, ending up with a fully professional quality document. Best of all you don&#8217;t even need to write any code to access the web service. The latest release of the Zend Framework (1.10) includes a component, Zend_Service_LiveDocx, to access and work with the service and there&#8217;s also a .NET library to download for Microsoft developers.</p>
<h2>Creating Templates</h2>
<p>As mentioned earlier the LiveDocx service uses mail merge fields to perform its magic. You create a document that contains one or more merge fields and then assign content to these fields programmatically at run time. The LiveDocx servers replace the mail merge fields with the content in the same way as a word processing program would, making the completed document available to you. Templates can either be stored directly on the LiveDocx servers or on your own webserver. The SOAP service allows to you specify the name of the template you want to use and whether it is stored locally or remotely. Which one is  chosen will largely depend on the application but storing templates on your own web server does mean that the template must be sent to LiveDocx along with the content before the completed document can be retrieved. This obviously significantly increases the amount of information that needs to be transmitted over the internet but the demands of your application may dictate that templates be stored locally.</p>
<h2>Using Zend_Service_LiveDocx</h2>
<p>The Zend Framework LiveDocx component is very easy to use and complete documentation can be found <a href="http://framework.zend.com/manual/en/zend.service.livedocx.html">here</a>. The Zend Framework download also includes several examples of using the service. Basically you instantiate a Zend_Service_LiveDocx_MailMerge object, setting your username, password and the template you wish to use, before assigning content to the object in much the same way that you would with Smarty. You then call a createDocument() method to produce the document on the remote server followed by a retrieveDocument() method to get the document produced back as a string. The latter method takes an argument which specifies the type of file that should be created. That&#8217;s basically it. The code below is a simple view class that I wrote for my application that uses the Zend_Service_LiveDocx to produce a document.</p>
<pre class="brush: php; title: ; notranslate">

&lt;?php
 /**
 * View for the download plan functionality. Uses the LiveDocx web service and the ZendFramework LiveDocx SOAP integration to produce a file.
 * @author Jeremy Cook
 * @version 1.0
 * @see http://framework.zend.com/manual/en/zend.service.livedocx.html
 */
 class planView {
 /**
 * Array of content to be used in the plan
 *
 * @var array
 */
 protected $content = array();
 /**
 * The type of file to be requested from LiveDocx
 *
 * @var string
 */
 protected $fileType;
 /**
 * Name of the template on LiveDocx to use
 *
 * @var string
 */
 protected $template;
 /**
 * Username for LiveDocx
 *
 * @var string
 */
 protected $username;
 /**
 * Password for LiveDocx
 *
 * @var string
 */
 protected $password;
 /**
 * Name to give the downloaded file
 *
 * @var string
 */
 protected $filename;
 /**
 * Constructor
 *
 * @param array $content
 * @param string $fileType Type of file to be requested from LiveDocx
 * @param string $template Name of the remote template to use on LiveDocx
 * @param string $filename Name to use for the file produced by LiveDocx
 * @param string $username
 * @param string $password
 * @return planView
 */
 public function __construct (array $content, $fileType, $template, $filename, $username, $password) {
    $this-&gt;content = $content;
    $this-&gt;fileType = $fileType;
    $this-&gt;template = $template;
    $this-&gt;filename = $filename;
    $this-&gt;username = $username;
    $this-&gt;password = $password;
 }
 /**
 * Method to produce the plan document
 *
 * @throws Zend_Service_LiveDocx_Exception
 * @throws RuntimeException
 */
 public function getPlan () {
    require_once 'Zend/Service/LiveDocx/MailMerge.php';
    $doc = new Zend_Service_LiveDocx_MailMerge();
    $doc-&gt;setUsername($this-&gt;username)-&gt;setPassword($this-&gt;password);
    $doc-&gt;setRemoteTemplate($this-&gt;template);
    foreach ($this-&gt;content as $key =&gt; $value) {
       $doc-&gt;assign($key, $value);
    }
    $doc-&gt;createDocument();
    $document = $doc-&gt;retrieveDocument($this-&gt;fileType);
    unset($doc);
    $output = fopen('php://output', 'w');
    if (!$output)
       throw new RuntimeException('Unable to bind PHP output as a file handle');
    switch ($this-&gt;fileType) {
       case 'pdf':
          header ('Content-Type: application/pdf');
          break;
       case 'doc':
       case 'docx':
          header ('Content-Type: application/vnd.ms-word');
          break;
    }
    $filename = str_replace(' ', '_', $this-&gt;filename) . &quot;.{$this-&gt;fileType}&quot;;
    header (&quot;Content-Disposition: attachment; filename=\&quot;$filename\&quot;&quot;);

    //Make sure the browser doesn't cache the file
    //Set headers to ensure the document is not cached.
    header (&quot;Expires: Mon, 26 Jul 1990 05:00:00 GMT&quot;);
    header (&quot;Last-Modified: &quot; . gmdate(&quot;D, d M Y H:i:s&quot;) . &quot; GMT&quot;);
    header (&quot;Cache-Control: no-cache, must-revalidate&quot;);
    header (&quot;Pragma: no-cache&quot;);
    //Write the document to php output
    fwrite($output, $document);
    fclose($output);
 }
 }
?&gt;
</pre>
<h2>Some &#8216;Gotchas&#8217;</h2>
<p>The two main problems I had in using the LiveDocx service had nothing to do with LiveDocx or Zend_Service_LiveDocx (which has great documentation). The problems I had were in making MS Word do what I wanted in the template creation. Admittedly this may have more to do with the fact that I&#8217;d never created a mail merge template before but I thought I&#8217;d mention the two issues here. The issues were in adding merge fields to the document and creating bookmarks.</p>
<h3>Adding Merge Fields</h3>
<p>Merge fields are denoted by the text {MERGEFIELD fieldName} (where &#8216;fieldName&#8217; is the unique name of the field). What the documentation doesn&#8217;t say however is that the curly braces cannot be simply typed on the keyboard. In Word you need to enter ctrl + F9 to get a special set of merge field curly braces. In Word 2007 you can also go to the insert tab then Quick Parts, [field] and select merge field. Add a name for the merge field and it is inserted into the document where your cursor is placed. This second method makes the field look like «fieldName» but this is completely equivalent to the first method with the curly braces.</p>
<h3>Adding Bookmarks</h3>
<p>With LiveDocx a bookmarked section allows you to iterate over a section of a template, assigning content from an array to merge fields inside the bookmark on each iteration. This enables you to produce sections of documents that have the same structure but different content. This is obviously very handy as varying amounts of content can be assigned to the section of the template. One of the examples in the Zend Framework utilises this to produce a telephone bill where a varying number of calls can be assigned to a bookmarked section, producing a table of all the calls in the final document. The array has to be multi-dimensional, with the top level array being numeric and each nested array being associative where the key is the name of the merge field and the value is the content to be inserted. What the documentation doesn&#8217;t tell you is how to create the bookmarks (there&#8217;s supposed to be a screenshot in the documentation which is missing) and I spent a few frustrating hours before I finally found out how to do it. Here are the steps.</p>
<ol>
<li>Go to the insert tab in Word and select bookmark.</li>
<li>Create a new bookmark that begins with blockStart_ (ie. blockStart_bookmarkName).</li>
<li>The bookmark will be invisible in the document but go ahead and add your merge fields with whatever formatting you like.</li>
<li>After the last merge field for the section add another bookmark but start it with blockEnd_ (ie. blockEnd_bookmarkName).</li>
<li>When you assign content to the bookmarked section make sure that the array is assigned to the name of the bookmark, like the following:</li>
</ol>
<pre class="brush: php; title: ; notranslate">

&lt;?php

//Assume $doc is a Zend_Service_LiveDocx_MailMerge object and $array is the multidimensional array to be assigned to the bookmark.

$doc-&gt;assign('bookmarkName', $array);

?&gt;
</pre>
<h2>Conclusion</h2>
<p>I hope you find this brief introduction to LiveDocx useful. I&#8217;ve found it to be a great service that&#8217;s easy to use and I&#8217;ll certainly use it again the next time I need to produce professional quality documents from within PHP.</p>
]]></content:encoded>
			<wfw:commentRss>http://jeremycook.ca/2010/05/16/creating-professional-documents-the-easy-way/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

