<?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>HirdWeb &#187; cakePHP</title>
	<atom:link href="http://www.hirdweb.com/tag/cakephp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.hirdweb.com</link>
	<description>Another Blog clogging up the already crowded internet</description>
	<lastBuildDate>Wed, 18 Jan 2012 20:54:49 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Count the Number of Cakes &#8211; Finding complex results with CakePHP</title>
		<link>http://www.hirdweb.com/2011/05/10/count-the-number-of-cakes-finding-complex-results-with-cakephp/</link>
		<comments>http://www.hirdweb.com/2011/05/10/count-the-number-of-cakes-finding-complex-results-with-cakephp/#comments</comments>
		<pubDate>Wed, 11 May 2011 03:23:27 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[custom queries]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=663</guid>
		<description><![CDATA[CakePHP offers a good selection of tools to help you retrieve the data. Recently, I came into a situation where I needed to find and paginate results based on a single, distinct column in the table. Distinct data can be tricky, especially if the tools do not allow you to select the distinct based on [...]]]></description>
			<content:encoded><![CDATA[<p>CakePHP offers a good selection of tools to help you retrieve the data. Recently, I came into a situation where I needed to find and paginate results based on a single, distinct column in the table. Distinct data can be tricky, especially if the tools do not allow you to select the distinct based on a column. Distinct will check all columns returned, and coupling in time stamps, 99% of the time all rows will be distinct. So how do you grab the data? Well, first lets examine the sample data that is needed to be extracted first. </p>
<p>The sample data is in a MS SQL Server database. The table contains a record ID, title id, author id, genre, type, last check out date, and edit date. It is possible to have duplicate title, author IDs in the table. We need to extract all DISTINCT title IDs, along with the other information listed where the type is not a paperback, and provide a paginated list. I am sure this would be better architected if needed in the real world. Paginate will only get us so far, as this would only show all records. </p>
<pre>class BooksController extends AppController {
    var $paginate = array(
        'order'        => array('Book.id' => 'desc'),
        'fields'    => array('Book.id', 'Book.title_id', 'Book.author_id', 'Book.genre_id', 'Book.type', 'Book.check_date', 'Book.edit_date'),
        'limit'        => 15,
    );
    . . .
    function index(){
        $this->set('hardback_books', $this->paginate());
    }
}</pre>
<p>We need to use more to build a conditional query so the paginate will query against this. We can use CakePHP&#8217;s data source to help in this. Now, we could also just write this query out ourselves, but this is helpful to know so when you have to build sub-queries for other items. All data is in MS SQL Server, and we can use normal SQL expressions, but we need to grab DISTINCT data, which goes by rows, not columns, which means we will need to do 2 sub-queries in addition to the main one. So we first need to grab a list of the TOP 1 items. This will be our inner query. </p>
<pre>        SELECT TOP 1 *
        FROM [books] AS [bk_inner]
        WHERE
            [bk_inner].[title_id] = [Book].[title_id]
            AND
            [bk_inner].[type] <> 'paperback' </pre>
<p>Next, we need to encapsulate that query with an outer one that will select all items which match up to the main query ID. </p>
<pre>    SELECT * FROM
    (
        SELECT TOP 1 *
        FROM [books] AS [bk_inner]
        WHERE
            [bk_inner].[title_id] = [Book].[title_id]
            AND
            [bk_inner].[type] <> 'paperback'
    ) AS [bk_outer]
    WHERE bk_outer.[title_id] = Book.[title_id] </pre>
<p>So we have the queries, and it needs the main query needs to constrain the results that exists in the sub-queries. </p>
<pre>SELECT TOP 15
    [Book].[id],
    [Book].[title_id],
    [Book].[author_id],
    [Book].[genre_id],
    [Book].[type],
    CONVERT(VARCHAR(20), [Book].[check_date], 20)
    CONVERT(VARCHAR(20), [Book].[edit_date], 20)
FROM [books] AS [Book]
WHERE EXISTS
(
    SELECT * FROM
    (
        SELECT TOP 1 *
        FROM [books] AS [bk_inner]
        WHERE
            [bk_inner].[title_id] = [Book].[title_id]
            AND
            [bk_inner].[type] <> 'paperback'
    ) AS [bk_outer]
    WHERE bk_outer.[title_id] = Book.[title_id]
)
ORDER BY [Book].[id] desc</pre>
<p>We have the final full query. Now how do we get that? First, we need to invoke the getDataSource() method. </p>
<pre>class Book extends AppModel {
    . . .
    function getHardbackBooks(){
        $dbo = $this->getDataSource();
</pre>
<p>Next we need to use the buildStatement() to build each statement. Since CakePHP will build a sub query with this, we have to do this twice: once for the inner query, and once for the outer query. The &#8220;table&#8221; for subquery2 will actually be subquery1, so we need to add that as a &#8220;table&#8221; in the array. </p>
<pre>$subquery1 = $dbo->buildStatement(
	array(
		'fields' => array('TOP 1 *'),
        'table' => $dbo->fullTableName($this),
        'alias' => 'bk_inner',
        'limit' => null,
        'offset' => null,
        'joins' => array(),
        'conditions' => 'bk_inner.title_id = Book.title_id AND bk_inner.type <> \'paperback\'',
        'order' => null,
        'group' => null

	),
	$this
);

$subQuery2 = $dbo->buildStatement(
    array(
        'fields' => array('*'),
        'table' => '(' . $subquery1 . ')',
        'alias' => 'bk_outer',
        'limit' => null,
        'offset' => null,
        'joins' => array(),
        'conditions' => 'bk_outer.[title_id] = Book.[title_id]',
        'order' => null,
        'group' => null
    ),
    $this
);</pre>
<p>Now, we need to make sure we add an EXISTS:</p>
<pre>$subQuery = ' EXISTS (' . $subQuery2 . ') ';
return $subQuery;</pre>
<p>Return the data from the model to the controller. In the controller function we need to add a new condition to the paginate. In the conditions, we do not need to use a paired item value to set it, we can use the straight SQL returned from the model. </p>
<pre>class BooksController extends AppController {
    var $paginate = array(
        'order'        => array('Book.id' => 'desc'),
        'fields'    => array('Book.id', 'Book.title_id', 'Book.author_id', 'Book.genre_id', 'Book.type', 'Book.check_date', 'Book.edit_date'),
        'limit'        => 15,
    );
    . . .
    function index(){
        <b>$data = $this->Book->getHardbackBooks();
        // Set to the paginate object conditions
        $this->paginate['conditions'] = array($data);</b>
        $this->set('hardback_books', $this->paginate());
    }
}</pre>
<p>And it returns the items based on the paginate parameters, ready to use in the view. It provides a DISTINCT list. And yes, I know I used more than 400 words in this one. It was closer to 500 without the code. Oh well, maybe tomorrow will be shorter. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2011/05/10/count-the-number-of-cakes-finding-complex-results-with-cakephp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Have Your Cake and Eat It Too</title>
		<link>http://www.hirdweb.com/2011/04/26/have-your-cake-and-eat-it-too/</link>
		<comments>http://www.hirdweb.com/2011/04/26/have-your-cake-and-eat-it-too/#comments</comments>
		<pubDate>Wed, 27 Apr 2011 00:15:23 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=573</guid>
		<description><![CDATA[CakePHP is one of those frameworks where it is easy to set up and get an application running in a minimal amount of time. It provides different securities, helps, and functions in the framework so that your application can run smoothly and be safe. As with all applications, the level of security and functionality depends [...]]]></description>
			<content:encoded><![CDATA[<p>CakePHP is one of those frameworks where it is easy to set up and get an application running in a minimal amount of time. It provides different securities, helps, and functions in the framework so that your application can run smoothly and be safe. As with all applications, the level of security and functionality depends on the developer, not the code, not the language, not the database. An application is only as secure, functional and reliable as the person/team who is coding it. One of the reasons I do like Cake is that the built in security and helpers offer a developer a great way to secure data, validate it, and display it. And that can also be one of the more trickier parts of getting the application to work correctly, finding the data to do something with it. </p>
<div id="attachment_575" class="wp-caption alignleft" style="width: 160px"><a href="http://www.hirdweb.com/wp-content/uploads/2011/04/new.png"><img src="http://www.hirdweb.com/wp-content/uploads/2011/04/new-150x150.png" alt="CakePHP" title="CakePHP" width="75" height="75" class="size-thumbnail wp-image-575" /></a><p class="wp-caption-text">CakePHP</p></div>
<p>CakePHP provides some functionality for finding the data, this is done using the &#8220;find&#8221; method. You can read more about this at the <a href='http://book.cakephp.org/#!/view/1017/Retrieving-Your-Data' target='_blank'>Cookbook</a>. Using this, one can grab data from many different tables if needed, or just one table. For this exercise, I am going to use dummy data to show how to find data, using simplistic finds, and using joins, and sub-queries. So first lets examine the data tables. Not all of these are going to be connected. This is a very simplistic, quickly drawn up solution to a lending library</p>
<div id="attachment_583" class="wp-caption aligncenter" style="width: 257px"><a href="http://www.hirdweb.com/wp-content/uploads/2011/04/tables2.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2011/04/tables2-247x300.jpg" alt="Sample Tables" title="Sample Tables" width="247" height="300" class="size-medium wp-image-583" /></a><p class="wp-caption-text">Sample Tables</p></div>
<p><span id="more-573"></span></p>
<p>In the image above, shows some sample tables, and these are just made up, and contain sample rows that may exist in those tables. The BOOKS and AUTHORS tables are a HABTM relationship, with a connecting table. The PUBLISHERS has many BOOKS. And the INVEN_ITEMS contains a multiple list of books that show whether the book is available or not. So we can start grabbing the data. </p>
<p>First we need to get a count for all books. </p>
<pre>$total_books = $this->Book->find('count'); </pre>
<p>Now grab all authors, age, bio, book name, total pages, publish date for a single book with the ID of 212:</p>
<pre>$details = $this->Book->find('first',
    array(
        'conditions' => array(
            'Book.id' => '212',
        ),
        'fields' =>array(
            'Book.name',
            'Book.pages',
            'Book.publish_date',
            'Author.name',
            'Author.age',
            'Author.bio',
        ),
        'recursive' => 0,
    )
);</pre>
<p>This will create a query statement to grab the data for the set fields, and only for the book id that equals 212. Recursive is set to 0 so it will grab all related data. Remember that this is a HABTM item, so CakePHP will take that into account and grab all authors for the book. We can leave off the recursive, I just find it is best practice to put this in in order to make the query more readable by someone who did not develop the application. </p>
<p>Find all Publishers:</p>
<pre>$publishers = $this->Publisher->find('all'); </pre>
<p>Now three example of using the find technique in a basic way. More examples are available in the cookbook. Now what if we need to grab all the detailed information on the inventory items, and the tables are not connected in a CakePHP relationship? Without using a framework, the query statement would need to contain JOIN statements to get all the data we need. So the following is an example of this, again getting detailed info for the Book ID of 212:</p>
<pre>$details = $this->find('first',
    array(
        'joins' =>array(
            array(
                'table' => 'books',
                'alias' => 'Book',
                'type' => 'LEFT',
                'conditions' => array(
                    'Book.id = Inven_item.book_id',
                ),
                'recursive' => -1
            ),
            array(
                'table' => 'books_authors',
                'alias' => 'BookAuthor',
                'type' => 'LEFT',
                'conditions' => array(
                    'BookAuthor.book_id = Book.id',
                ),
                'recursive' => -1
            ),
            array(
                'table' => 'authors',
                'alias' => 'Author',
                'type' => 'LEFT',
                'conditions' => array(
                    'Author.id = BookAuthor.author_id',
                ),
                'recursive' => -1
            ),
            array(
                'table' => 'publishers',
                'alias' => 'Publisher',
                'type' => 'LEFT',
                'conditions' => array(
                    'Publisher.id = Book.publisher_id',
                ),
                'recursive' => -1
            ),
        ),
        'conditions' => array(
            'InvenItem.book_id' => '212',
        ),
        'fields' =>array(
            'Book.name',
            'Book.pages',
            'Book.publish_date',
            'Book.quick_overview',
            'Author.name',
            'Author.age',
            'Author.bio',
            'Author.country',
            'Publisher.name',
            'Publisher.location',
            'Publisher.state',
            'Publisher.country',
            'InvenItem.isbn',
            'InvenItem.available',
        ),
        'recursive' => -1
    )
);</pre>
<p>For whatever reason, the Inventory was not connected to the other tables, and so we force a join on the query. In the JOINS, we set this to a -1 recursive so it does not pull in all the other data. Now this is only one solution to put an example of a JOIN on a query. We have the tables, joined in tables, conditions, and recursive set. Once you get the data, then it will show all inventory items, details for the book, and whether or not the copy is available at the present time. </p>
<p>Now this may not be the best way to grab the data, and this is just for example purposes as well. Next week I will go further into detail about setting multiple conditions using &#8216;OR&#8217; instead of &#8216;AND&#8217;, creating a simple search on possible conditional search terms and areas, and I may even create a quick little app to demonstrate this in real life.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2011/04/26/have-your-cake-and-eat-it-too/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Data Model Relationships &#8211; CakePHP&#8217;s HABTM</title>
		<link>http://www.hirdweb.com/2011/03/15/data-model-relationships-cakephps-habtm/</link>
		<comments>http://www.hirdweb.com/2011/03/15/data-model-relationships-cakephps-habtm/#comments</comments>
		<pubDate>Tue, 15 Mar 2011 15:04:59 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[data models]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=531</guid>
		<description><![CDATA[For today, lets dive back into some code, well data modeling at least. When you set up an application that connects to a database, you need to understand the data that will be working in the application. This is the data that will be edited, added, read and even scrutinized int he application. When looking [...]]]></description>
			<content:encoded><![CDATA[<p>For today, lets dive back into some code, well data modeling at least. When you set up an application that connects to a database, you need to understand the data that will be working in the application. This is the data that will be edited, added, read and even scrutinized int he application. When looking at the application data, one could easily put all data in a table and make it as flat as possible. We could normalize it until the cows come home as well. What is the best choice? My vote is always plan for what is best for the application, and the future of the application. When it comes to data, a more normalized data layout is always going to provide better performance and better ability to scale in the future. In our little example application, we are going to model the data for an online movie rental inventory. We will take an example film: <a href='http://www.imdb.com/title/tt1205489/' target='_blank'>Gran Torino</a> to help the example model.</p>
<p>The data we need for this application includes some basic information: movie title, genre(s), stars, directors, writers, story information, rating, release year, rent price. We can include a lot more data if we really needed to, but for the purpose of this, we will keep it a little simple. A possible way of modeling this data is to create a table that stores all of this information, and have one table in the database. But now when we need to add something else, we have to add columns to the table. For example, in a few months the company decided to add related titles, sequels and sets, etc. It would require a refactor of the data in order to handle this, as well as refactor of the code. So lets split this out. </p>
<p>In the image below, I divided the content based on a few things: Title data, Talent Data, Genre Data, Rating Data<br />
<div id="attachment_535" class="wp-caption aligncenter" style="width: 529px"><a href="http://www.hirdweb.com/wp-content/uploads/2011/03/table1a.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2011/03/table1a.jpg" alt="Starting the data model design" title="Table Set Up" width="519" height="235" class="size-full wp-image-535" /></a><p class="wp-caption-text">Starting the data model design</p></div></p>
<p>I now have the four main tables, but we need to figure out how these are related. First lets tackle the Rating Data, as that will be a simple design. I am linking to the IMDB so you can look at the data. The ratings available in the United States, at least the ones we will include, are: G (General Audiences), PG (Parental Guidance suggested), PG-13 (Parents strongly cautioned) and R (Restricted, no one under 17 allowed without a parent, or as I call it, PG-17). So each rating will be housed in this table. We will need an identifier, the rating, the explanation, and some data to track creation and modification. We do not need those last two, but it is just good practice to include those if there is ever going to modification on data. Using our example film, it is rated &#8220;R&#8221;. And since any film title object (Titles) will ever only have one rating (for the sake of this example) there is an easy relation of a hasOne relation to the Ratings table. We need to add a foreign key to the Titles table and connect these.<br />
<div id="attachment_538" class="wp-caption aligncenter" style="width: 356px"><a href="http://www.hirdweb.com/wp-content/uploads/2011/03/table2.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2011/03/table2.jpg" alt="hasOne Relation to Ratings" title="hasOne Relation to Ratings" width="346" height="291" class="size-full wp-image-538" /></a><p class="wp-caption-text">Title hasOne Rating</p></div> </p>
<p>Easy to connect those. Now, we need to tackle the Genres. This is a little more complicated, but we can get through this. The Genres table will house the genres we need to display. This list can be as big or small as needed. Our example movie is in the &#8220;Drama&#8221; genre according to IMDB. However, in our application, the business has decided the movie is classified as Drama and Action. So now a title is going to have many genres. And a genre can belong to many titles. The &#8220;Drama&#8221; genre may belong to multiple titles. So we can not just add a new column to the Titles table, as that will not satisfy the requirements. We need to add a connecting table, and according to the naming convention of CakePHP, the name of the connector table is the alphabetical order of the two tables it is connecting. </p>
<p>So we need to add a table titled &#8220;Genre_Titles&#8221;. It will have an ID, and foreign keys to both tables.<br />
<div id="attachment_540" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.hirdweb.com/wp-content/uploads/2011/03/table3.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2011/03/table3-300x155.jpg" alt="HABTM Genres" title="HABTM Genres" width="300" height="155" class="size-medium wp-image-540" /></a><p class="wp-caption-text">Connecting the Genre and Title tables</p></div></p>
<p>Now we are almost done with the HABTM set up. We made it through one of them, and that was a good thing. See it was not so difficult. Now, we need to finish this up, and connect the talent table to the title. Talent can be anything. Since the company wants to display the stars of the show, the directors and writers, we need to be able to connect these. And again, this will require a a HABTM relationship. An actor can be in many titles, just like Clint Eastwood, as he was not just in Gran Torino. So he may be listed in many titles. And, with this movie, he not only stars in it, he directed it. So now we not only need to match up this talent, we have to identify it correctly. So this adds a little complexity to this, but we can do this. </p>
<p>As you know from the previous example, we need to create a connecting table. The name would be &#8220;Talent_Titles&#8221;. But that still will not solve the issue of identifying Clint Eastwood as an actor and director in the title. We can add a new table &#8220;Talent_Types&#8221;. This will be a &#8220;lookup table&#8221; that houses Star, Director, Writer as values. We can then connect that to the connecting table. This relationship will be a hasMany to Talent_Titles, as a star may have many entries in the Talent_Title table.<br />
<div id="attachment_542" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.hirdweb.com/wp-content/uploads/2011/03/table4.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2011/03/table4-300x255.jpg" alt="HABTM Talent and Title" title="Final Data Model" width="300" height="255" class="size-medium wp-image-542" /></a><p class="wp-caption-text">HABTM Talent and Title</p></div></p>
<p>And that is the HABTM design. Using the Bake method, you can now bake this up, and set up the model. The thing to remember about the HABTM, it is not something to fear. Usually, if a connecting table is needed, you have a HABTM design. Remember to think in human terms when examining the data model. What does this belong to, what does it have. In this, the Title will have many actors, directors, etc. And the actors, directors, etc will belong to many titles. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2011/03/15/data-model-relationships-cakephps-habtm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thinking of the site</title>
		<link>http://www.hirdweb.com/2011/03/05/thinking-of-the-site/</link>
		<comments>http://www.hirdweb.com/2011/03/05/thinking-of-the-site/#comments</comments>
		<pubDate>Sat, 05 Mar 2011 17:40:29 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[Facebook]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=505</guid>
		<description><![CDATA[So as I am sitting here trying to get caught up on the StephenHird site, I am thinking a little more about it. It seems like it is just a huge issue right now and am not able to complete what I really want to complete on the site. So I may make an executive [...]]]></description>
			<content:encoded><![CDATA[<p>So as I am sitting here trying to get caught up on the StephenHird site, I am thinking a little more about it. It seems like it is just a huge issue right now and am not able to complete what I really want to complete on the site. So I may make an executive decision to abandon the code (not destroy it, but abandon it and store it safely if I want to use it again), start over from scratch, again using CakePHP, and the Facebook Graph API. This would be a little less intense, and would contain an easier example of how to get some data out there, and still include the Graph API in order to show examples of how to do this. </p>
<p>I still think the idea of the resume integrated with the API is a good one to show an example, but I am finding myself with less time to do a full blown app for it. And I figure, if I do a small example, that should be enough, and there is always the documentation available at Facebook for this. So if you have been following the Graph API integration, it still will happen, just in a different form. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2011/03/05/thinking-of-the-site/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting into the code</title>
		<link>http://www.hirdweb.com/2011/01/23/getting-into-the-code/</link>
		<comments>http://www.hirdweb.com/2011/01/23/getting-into-the-code/#comments</comments>
		<pubDate>Sun, 23 Jan 2011 19:04:34 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=472</guid>
		<description><![CDATA[So it has been a couple of weeks since I have posted. But now we need to set up some base code before we can go forward with the details and then adding in the Facebook Graph API. In the last post, the Data model was set up. We have skills and certifications as standalone [...]]]></description>
			<content:encoded><![CDATA[<p>So it has been a couple of weeks since I have posted. But now we need to set up some base code before we can go forward with the details and then adding in the Facebook Graph API.  In the last post, the Data model was set up. We have skills and certifications as standalone tables. Skills with the levels and areas tables connected together. We also have the main glue of resumes, connected to covers and tasks which itself is connected to jobs. A lot of tables to create the resume section, but will keep some of this information all together. We need to create some code so that we can get all this information. </p>
<p>If you Baked each object, and used the Bake methods to create the model, and associations, as well as the controllers an views, you will have some code ready to use and ready to go. After you have Baked these items, the sample code that is created is ok to begin with. However, we want to take advantage of a very important technique, and the is the centralization of code, and prevent code duplication. There is one other thing that gets to me, and this is more of an OCD thing for me in code, and that is the way that Cake does the edit check in the controller. In the base created code, it creates a section of code that checks for an ID. If it is not passed, then it redirects the page elsewhere. Like so:</p>
<pre>function edit($id = null) {
    if (!$id &#038;&#038; empty($this->data)) {
        $this->Session->setFlash(__('Invalid resume', true));
        $this->redirect(array('action' => 'index'));
    }
    . . . .
}</pre>
<p>So in this code segment, if one gets to the edit form, and an ID is passed, and the form is not filled in, then it will display the actual form. And if the ID is not passed in, then it redirects to the Index page. For examples, if the site name was test.com:<br />
www.test.com/resume/edit/2 &#8211; will result in the form being shown<br />
www.test.com/resume/edit &#8211; will result in a redirect and the error message</p>
<p>Now here is where my OCD kicks in a little. . . .<br />
<span id="more-472"></span><br />
If the table is housing 3 resumes, IDs of 1,2, and 3 and you go to the edit page for those resumes, it will display the resume content. But, for kicks and giggles, what if we passed in this as the URL:<br />
www.test.com/resume/edit/350</p>
<p>This will display a blank form and even allow one to input data, fill it out, and submit it. Now granted, the processing of it alone will provide a new resume. However, that is why we have an &#8220;Add&#8221; function. Why not create some logic to check to see if the ID is valid to begin with? This will help on both the edit, and view methods in the controller. (as the View function will behave the same way as the edit function on display). And it is not just for this controller, but it will likely be an issue in all controllers. What I did in this case, is provide a function in the App Model code so that it can check those. Here is one way to solve this. And there can be multiple ways to resolve this issue, this is just one method I have done.<br />
In the app/app_model.php code:</p>
<pre>function ifExists($model = null, $id){
    if ( ($model == null) || ($model == '') ||(is_null($id)) ||($id == '') ){
        return 0;
    }
    $check = $this->find('count', array('conditions' => array($model . "." . strtolower($model) . '_id' => $id)));

    return $check;
}</pre>
<p>The way I set up my models for the primary key is tableName_id. So for the resume table, the primary key is &#8220;resume_id&#8221;, and for the skills table it is &#8220;skill_id&#8221; and so forth. You can always modify this to use whatever naming convention you use. In this function, it checks to see if the model is passed. If it is not, returns a false. If a model is passed, then it runs a quick find query to see if that ID exists in the table. If it finds anything, then it returns a &#8220;1&#8243; (or true), if not, then a &#8220;0&#8243; (or false). By sticking this in the app_model.php file, it is now accessible by all models. To use this, in the edit function, for example, in the resume controller:</p>
<pre>function edit($id = null) {
    if ( ((!$id) || (!$this->Resume->ifExists('Resume', $id))) &#038;&#038; empty($this->data)) {
        $this->Session->setFlash(__('That Resume does not seem to exist, please try this again.', true));
        $this->redirect(array('action' => 'index'));
    }
    . . .
}</pre>
<p>This will check for an ID, checks to see if that resume exists, and that the form is not filled in yet. If the ID does not exist in the table, then it redirects to the Index page, with a customized message. I could also redirect to the add page with no message, and this is entirely up to you on how you want to do this. </p>
<p>For the View function, it is very similar:</p>
<pre>function view($id = null) {
    if ( (!$id) || (!$this->Resume->ifExists('Resume', $id)) ) {
        $this->Session->setFlash(__('That Resume ID does not seem to exist. Please try again', true));
        $this->redirect(array('action' => 'index'));
    }
    . . .
}</pre>
<p>Again, check for an ID, and then make sure it exists in the table. If not, then redirect. A small bit of code to help keep the application from having odd behavior and keep it clean. </p>
<p>Next is to process the forms. By using Bake, it creates two functions, add and edit. Both process the forms as needed. Which there is nothing we can do to clean that up. However, some basic pre-commit tasks are still to be done. One thing I am highly in favor of is sanitizing the data. If there is some basic checks we need to do on the data, then we must also do that as well. For this we, will use the jobs controller/model for the example. </p>
<p>In each row of the job, we have a start date and an end date. In the model, we will validate these fields to be a &#8220;date&#8221;. We also need to validate these dates in another way. The end date can not be earlier than the start date. It is quite impossible to end a job three months before you even start it. So we want to keep that in check as well. And let&#8217;s say we want to do that in the controller. So now we need to sanitize the data, and check the dates. This can be redundant in the code, as it will exist in the edit and add functions. And if we need to make a change to the logic, it will cause an issue in two places. So, we can take that and put this in its own function. First we start with the sanitation of the data. At the top of the file, place</p>
<pre>App::import('Sanitize');</pre>
<p>Then create a new function and place the sanitation in this function:</p>
<pre>function validateForm($data){
    $san = new Sanitize();
    // Clean the data
    $this->data = $san->clean($this->data);
    $this->data['Job']['company_name'] = $san->paranoid($this->data['Job']['company_name'], array(' ', '.', '/', '-', '*', '&#038;', '(', ')',','));
    $this->data['Job']['via_company']  = $san->paranoid($this->data['Job']['via_company'], array(' ', '.', '/', '-', '*', '&#038;', '(', ')',','));
    $this->data['Job']['location']     = $san->paranoid($this->data['Job']['location'], array(' ', '.', '/', '-', '*', '&#038;', '(', ')',','));
     . . .
}</pre>
<p>In this, I am cleaning the Company Name, the Via Company (in case it was a contract job), and Location. The next thing we need to do is create the logic to test the dates.I am just going to create a simple number using the fields from the form. Then I will compare the 2 numbers, and if the end is less than the start, then it is an error. So the entire function is:</p>
<pre>function validateForm($data){
    $san = new Sanitize();
    // Clean the data
    $this->data = $san->clean($this->data);
    $this->data['Job']['company_name'] = $san->paranoid($this->data['Job']['company_name'], array(' ', '.', '/', '-', '*', '&#038;', '(', ')',','));
    $this->data['Job']['via_company']  = $san->paranoid($this->data['Job']['via_company'], array(' ', '.', '/', '-', '*', '&#038;', '(', ')',','));
    $this->data['Job']['location']     = $san->paranoid($this->data['Job']['location'], array(' ', '.', '/', '-', '*', '&#038;', '(', ')',','));
     . . .   

    $errors = array();

    // Set the dates in a usuable format
    $start = $data['Job']['start_date']['year'] . $data['Job']['start_date']['month'] . $data['Job']['start_date']['day'];
    $end   = $data['Job']['end_date']['year'] . $data['Job']['end_date']['month'] . $data['Job']['end_date']['day'];
    $today = date('Ymd'); 

    // Check to make sure the start date is not later than the end date
    if ( $start >= $end ){
        // $this->Job->invalidate('start_date', "The Start Date must be earlier than then End Date");
        $error['err']['field'] = 'start_date';
        $error['err']['messg'] = 'The Start Date must be earlier than then End Date';
    }

    // Check to see whether or not to include the end date as part of the insert
    if ( $end == $today ){
        $error['end'] = 0;
    } else {
        $error['end'] = 1;
    }
    $data[error] = $error;
    return $data;
}</pre>
<p>Now we need to be able to use that in the actual form. And we need to invoke an error in case there is an issue. If an error is found, we need to invalidate the field and provide the error message. That could be done in the following way:</p>
<pre>function edit($id = null) {
    . . .
    if (!empty($this->data)) {
        $check = $this->validateForm($this->data);
        // Check to make sure the start date is not later than the end date
        if ( isset($check['Error']['err']) ){
            $this->Job->invalidate($check['Error']['err']['field'], $check['Error']['err']['messg']);
        }

        // Set the data to the model now
        $this->Job->set( $check['Job'] );

        if ( $this->Job->validates() ) {
            // Do the normal process/save stuff
            . . .
        }
         . . .
    }
    . . .
}</pre>
<p>In this example, it shows how to find ways to cut down code repetition. Now I am sure we could even set this validation in the model as well. I am just giving an example of what we can do. Now using this, we can set up all the different functionality of the resume application. </p>
<p>In the next post, I will go through how to secure the Resume so only parts are seen by the world, and which parts you would have to log in for, and why. I will also go into a centralized query for all resume elements and be able to display.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2011/01/23/getting-into-the-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adding Comments in your Site with the Facebook API</title>
		<link>http://www.hirdweb.com/2010/10/30/adding-comments-in-your-site-with-the-facebook-api/</link>
		<comments>http://www.hirdweb.com/2010/10/30/adding-comments-in-your-site-with-the-facebook-api/#comments</comments>
		<pubDate>Sun, 31 Oct 2010 06:04:32 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[Facebook]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=430</guid>
		<description><![CDATA[Now that I have jumped almost 2 weeks without a post, as I have been super busy, this should have been a real easy item to post, but I wanted to make sure that this is done correctly. This is probably one of the easiest methods to add some great Facebook functionality in your site. [...]]]></description>
			<content:encoded><![CDATA[<p>Now that I have jumped almost 2 weeks without a post, as I have been super busy, this should have been a real easy item to post, but I wanted to make sure that this is done correctly. This is probably one of the easiest methods to add some great Facebook functionality in your site. This revolves around comments to a page. In my example, I am posting topics to discuss. This is mainly just a small little blurb that I will enter via an admin form on the site, and then list the different topics for everyone to select one. Once they select it, they can view the details of the topic and then comment on it using the Facebook API/Social plugin. So as always, lets go through a basic plan for this idea. </p>
<p>1. The model is Topic, with a table in the DB labeled &#8220;topics&#8221;<br />
2. Only the admin has access to add or edit the topics<br />
3. All comments on this topic will be done through the Facebook API/Social Plugin Comments<br />
4. Topics will have a title that will also double as the Unique ID (to be explained later)<br />
5. Topic titles, or themes, will not be allowed to be edited, to be explained why later<br />
6. Administration of the comments will be done by the Facebook Application admins, which differs from the site admins<br />
7. Start Dates will determine if the topic is allowed to be visible yet<br />
8. End dates are optional, and will be built upon later with more advanced FBML/FB JS libraries</p>
<p>And there it is, some basic ideas behind the whole idea. So now lets get into some of the items called out in Numbers 4 and 5<br />
<span id="more-430"></span><br />
With the Facebook Comments, you can get a base idea behind what this entails at the following location:<br />
<a href="http://developers.facebook.com/docs/reference/plugins/comments" target="_blank">http://developers.facebook.com/docs/reference/plugins/comments</a><br />
This will give you the basic comment box to add to the site. The important part of this comment box is the &#8220;Unique ID&#8221;. This will add all the comments to the basic page. However, it must be Unique and not change. In my requirements above, I am listing the topic title as the Unique ID. I could easily generate a number for this and add a couple of letters to it to make it truly unique and allow the title to change. In this example, I want to emphasize certain elements, so that is why I chose to do it this way. You can easily do this differently and still have it work. But this leads to Number 5. </p>
<p>The Unique ID should not change for the page, topic, area, etc. If the ID changes, all previous comments vanish and you basically are starting over. So it is important that the IDs are unique and do not change. This way the element of participation is intact. It will always stay in your site. </p>
<p>Another note. This example is showing how to include Facebook API in a CakePHP application. You can follow these same techniques for just about any site, no matter what framework. Just apply the same inclusion techniques in the proper location for the different framework/php site. </p>
<h3>Creating the base elements and DB table</h3>
<p>So, let&#8217;s get this thing going. First you should have a CakePHP site going and ready, connecting to a DB somewhere. Bake the Model, Controller and Views for this one. The SQL for the Topics that I am using is below:</p>
<pre>CREATE TABLE IF NOT EXISTS `topics` (
  `topic_id` int(11) NOT NULL auto_increment,
  `topic_slug` varchar(255) NOT NULL COMMENT 'Slug for use in the Facebook comments',
  `question` text NOT NULL COMMENT 'Question that is created for the different topics',
  `start_date` date NOT NULL COMMENT 'Start date for the question',
  `end_date` date default NULL COMMENT 'End date for the question',
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY  (`topic_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COMMENT='Topics for discussions and other items' AUTO_INCREMENT=1012 ;</pre>
<p>I baked this using the basic CRUD for the views and Controller, no Admin hooks, and the Model validation is as follows:</p>
<pre>class Topic extends AppModel {
    var $name = 'Topic';
    var $primaryKey = 'topic_id';
    var $displayField = 'topic_slug';
    var $validate = array(
        'topic_slug' => array(
            'minlength' => array(
                'rule' => array('minlength', 3),
                'message' => 'The Topic Identifier should be longer than 3 characters',
            ),
            'maxlength' => array(
                'rule' => array('maxlength', 50),
                'message' => 'The Topic Identifier should be no longer than 50 characters',
            ),
        ),
        'question' => array(
            'minlength' => array(
                'rule' => array('minlength', 3),
                'message' => 'You need to have a question at least 3 characters long',
            ),
        ),
        'start_date' => array(
            'date' => array(
                'rule' => array('date'),
                'message' => 'Please set a valid start date for this question',
            ),
        ),
    );
}</pre>
<h3>Controller work for the Index</h3>
<p>I removed all the links to Add, Edit and Delete from the views and kept those hidden except for the Admin. Now comes the controller. This is the important part of this, as we need to include the Facebook items, and based on the last post, we put those in the Vendors, so we need to include this. I am also using Sanitize, Auth and few others (Security and Email), along with the helpers of HTML and FORM.</p>
<pre>&lt;?php
App::import('Sanitize');
App::import('Vendor', 'facebook');
class TopicsController extends AppController {

    var $name = 'Topics';
    var $helpers = array('Html', 'Form');
    var $components = array('Security', 'Auth', 'Email');</pre>
<p>it is important to import the Facebook library. I will not go over the Auth in this example, or the beforeFilter or isAuthorized functions. The first action we need to deal with is the index() function. Originally, it looks like:
<pre>function index() {
        $this->Topic->recursive = 0;
        $this->set('topics', $this->paginate());
    }</pre>
<p>For our purposes, we want to limit the amount of posts shown to only those that have a start date that is equal to or less than today&#8217;s date. I have three examples in my DB, and you can create as many as you want for the example. Just make sure at least one is in the future. Right now when we look at the Index page, it is showing all topics. So we need to set different items in the call for paginate. To only grab those that are equal to or less than to today&#8217;s date, we will alter the call to paginate as follows:</p>
<pre>$today = date('Y-m-d');
$cond = array('Topic.start_date <=' => $today);
$this->set('topics', $this->paginate('Topic', $cond));</pre>
<p>This will limit all the posts to display to everyone to only those with a current (or past) start date. All those with a start date in the future will not be available until that date. But we also need to set a variable for the Admin to see all those topics waiting for the future. </p>
<pre>$future_topics = $this->Topic->find( 'all', array('conditions' => array('Topic.start_date >' => $today)) );
$this->set('futop', $future_topics);</pre>
<p>We now have 2 variables to work with in the view. You can do it a whole slew of different ways, and that is fine, this is just the method I prefer to do it. Now we need to get the view going. This is going to a little different, I have a few things to show Admin stuff just to admins, and show the future topics just to admins.</p>
<h3>The Add and Edit functions</h3>
<p>That does it for the view, now we need to do the Add and Edit functions. Remember we baked this, and let all the default take place. But, I need to get a valid Unique ID, and do not want any spaces in it. So we will alter the Add function to do this. And for the Edit function, we want to prevent any editing of the slug area. </p>
<pre>function add() {
        if (!empty($this->data)) {
            $san = new Sanitize();

            $this->data['Topic']['topic_slug'] = $san->paranoid($this->data['Topic']['topic_slug'], array(' '));

            $this->Topic->create();
            // Slug the topic title before saving
            $this->data['Topic']['topic_slug'] = preg_replace('/ /', '_', $this->data['Topic']['topic_slug']);
            if ($this->Topic->save($this->data)) {
                $this->Session->setFlash(__('The new Topic has been saved and will be put in queue', true));
                $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash(__('The Topic could not be saved. Please, try again.', true));
            }
        }
    }</pre>
<p>I am sanitizing the data for the slug, and trusting myself for the topic details. In real life, we would apply a sanitizing effect to this field as well. I then erase all non alphanumeric characters, and replace spaces with an underscore &#8220;_&#8221;. Then save it up. I am basically leaving the &#8220;add.ctp&#8221; alone with what was baked. I removed the fields that I really do not care about. </p>
<p>So now we move to the edit. We want to prevent any changes to the topic_slug, so in the form, we will remove this from the form and make sure we remove the underscores when showing it. The controller is:
<pre>function edit($id = null) {
        if ( ((!$id) || (!$this->Topic->ifExists('Topic', $id))) &#038;&#038; empty($this->data) ) {
            $this->Session->setFlash(__('The Topic you selected does not exist, please try again.', true));
            $this->redirect(array('action' => 'index'));
        }
        if (!empty($this->data)) {
            if ($this->Topic->save($this->data)) {
                $this->Session->setFlash(__('The topic has been saved', true));
                $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash(__('The topic could not be saved. Please, try again.', true));
            }
        }
        if (empty($this->data)) {
            $this->data = $this->Topic->read(null, $id);
        }
    }</pre>
<p>One quick thing to call out in this function. I added a new function to the parent app_model.php to make sure that the ID we are trying to edit exists and is valid. The basic baked function only checks for an ID, if one exists, it goes thru to the form, even if the ID does not exist in the DB. On the edit.ctp view, I removed the form element for the topic_slug and added this:</p>
<pre>$topic_title = preg_replace('/_/', ' ', $this->data['Topic']['topic_slug']);
echo "&lt;b&gt;Topic Title&lt;/b&gt;: &nbsp; &lt;h4&gt;" . ucwords($topic_title) . "&lt;/h4&gt;";</pre>
<p>And now the edit form works, keeps the underscore in and saves the form. Again, in the real world, you would want to do a little more on the back end to ensure there is no changes being made. But now we get to the view area and where the Facebook API/Social Plugin comes in. </p>
<h3>Creating the Detail View and adding in Facebook API/Social Plugin</h3>
<p>So here is where we get to the meat of the post. All other areas were in preparing to get here. Now, all you need to do is create a few different topics to work with. We want to alter the view in such a way it will display only the items we need, and will only display the topic to an admin if it is a future topic. Otherwise return the user to the index for topics with a message saying that the topic does not exist or is not yet available. So the view function is simple:
<pre>function view($id = null) {
        if ( (!$id) || (!$this->Topic->ifExists('Topic', $id)) ) {
            $this->Session->setFlash(__('The requested topic does not exist. Please try again.', true));
            $this->redirect(array('action' => 'index'));
        }
        // Make sure this is a valid ID as well
        $topic = $this->Topic->read(null, $id);
        $topic_title = $topic['Topic']['topic_slug'];
        // Set the title without the slug
        $topic_title = preg_replace('/_/', ' ', $topic['Topic']['topic_slug']);
        $topic['Topic']['topic_title'] = ucwords($topic_title);

        // make sure this topic is valid
        if ( ($topic['Topic']['start_date'] > date('Y-m-d')) &#038;&#038; (!$this->is_admin) ){
            $this->Session->setFlash(__('The requested topic does not seem to be available at the present moment. Try again later.', true));
            $this->redirect(array('action' => 'index'));
        }
        $this->set('topic', $topic);
    }</pre>
<p>Again, I used the app_model.php function to make sure the topic ID exists as well as being valid. If not, a message will be displayed to the end user. It sets the topic, and now we are ready for the view. </p>
<p>So what I did, was wipe all the data for the list, and added my own. I set the title to the H2 element. I set the question to a paragraph tag, and added the new FBML comment tag. The information for this tag can be found at:<br />
<a href='http://developers.facebook.com/docs/reference/fbml/comments_%28XFBML%29' target='_blank'>http://developers.facebook.com/docs/reference/fbml/comments_%28XFBML%29</a></p>
<p>Pay close attention to the different parameters you can set and how you can format this to fit your style on your site. I have set the following elements and values:<br />
xid = $topic['Topic']['topic_slug']<br />
width = 600<br />
title = $topic['Topic']['topic_title']<br />
publish_feed = false</p>
<p>And so the actual markup added to the page is the following, including the FBML script and div tag:</p>
<pre>&lt;div id="fb-root"&gt;&lt;/div&gt;
&lt;script src="http://connect.facebook.net/en_US/all.js#appId=APP_ID&amp;xfbml=1"&gt;&lt;/script&gt;
&lt;fb:comments xid="&lt;?php echo $topic['Topic']['topic_slug']; ?&gt;" width="600" title="&lt;?php echo $topic['Topic']['topic_title']; ?&gt;" publish_feed="false">&lt;/fb:comments&gt;</pre>
<p>And if you go to the page you will now see the comments section, but we are missing a very important item in the comments, and that is administration. The way to add this, we will need to add the correct element to the page. This consists of adding the correct JS API to the site. </p>
<p>According to the documentation, you have to be listed as a developer for the application to be able to administer comments. The way we have it set up right now, we can have comments, but there is no way we can administer those. The key to this is very simple and easily overlooked. In the FBML markup that we added to the view, you will notice a specific section:</p>
<pre>http://connect.facebook.net/en_US/all.js#<b>appId=APP_ID</b>&amp;xfbml=1</pre>
<p>You must input your application ID to this markup. Once you do that, and if you are listed as a developer, then you are able to now administer comments on your site. </p>
<p>See, that was easy. There is a lot of text on this post, and most of it has to do with setting up CakePHP to do this. Once we got to the Facebook API/Social Plugin section, it was very simple. With the next post, we will expand on the comments section a little further and see what else is possible. </p>
<p>You can see this all in action at the following:<br />
<a href='http://www.stephenhird.com' target='_blank'>www.stephenhird.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2010/10/30/adding-comments-in-your-site-with-the-facebook-api/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Facebook Application on the Site</title>
		<link>http://www.hirdweb.com/2010/10/17/facebook-application-on-the-site/</link>
		<comments>http://www.hirdweb.com/2010/10/17/facebook-application-on-the-site/#comments</comments>
		<pubDate>Sun, 17 Oct 2010 15:21:08 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[Facebook]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=390</guid>
		<description><![CDATA[OK, I finally got my data models set up and working. I have the initial CakePHP set up on the site, it is using v1.3, and now I am ready to set it up for the Facebook integration, and start to add the integration. When we first set up the application on the Facebook side, [...]]]></description>
			<content:encoded><![CDATA[<p>OK, I finally got my data models set up and working. I have the initial CakePHP set up on the site, it is using v1.3, and now I am ready to set it up for the Facebook integration, and start to add the integration. When we first set up the application on the Facebook side, I chose to do an &#8220;iframe&#8221; version of the application, as I want Facebook on my site, and be able to have integration with some of the great Facebook tools on the site, and be able to &#8220;promote me&#8221;. And remember this is just a way to show a possible real world example of how to integrate these things with your site. Actual applications may vary, but this is the base to integrate. At least, as of this posting it is the base, it may change in the future.</p>
<p>So lets go ahead and dive into it. If you do not have the application ID for your Facebook application, you can get it at the following:</p>
<p>http://www.facebook.com/developers/apps.php</p>
<p>The next thing is to grab the API and code from Facebook. This can be found at the following page:</p>
<p>http://developers.facebook.com/docs/</p>
<p>This is the main page, and you will need to scroll to the bottom of the page. This will list different APIs that are available. I am going to be using the PHP and JavaScript SDKs. This will provide the back end that I will want, and will also provide a positive user experience on the front end. So be sure to download both SDKs.</p>
<p>After that, now we need to start getting some stuff set up. In this post, I am just going to explain how to get this set up, and working right now. It is important that we get the correct items working, and so we will be working with the &#8220;pages&#8221; area for the JS SDK, and creating a very simple controller for the PHP SDK so we can get set up and running. I am just using, for right now, the base CakePHP CSS styles and layouts. All we need is a page to display some of the basic items to ensure that we have installed the SDKs in the proper locations. So lets go.<br />
<span id="more-390"></span><br />
<strong> Step 1: </strong><em>Download the SDK and move them to the following locations:</em><br />
A. PHP SDK (facebook.php)<br />
I put this at the following location:<br />
./app/vendors/facebook.php<br />
This will be used in the controllers and be imported by the controllers</p>
<p>B. JavaScript SDK<br />
I put the entire &#8220;src&#8221; directory in the following location:<br />
./app/webroot/js/src</p>
<p><strong>Step 2: </strong><em>Create a &#8220;test&#8221; page where we can see the Facebook JS SDK to make sure it works</em><br />
I just created a new file:<br />
./app/views/pages/facebook.ctp<br />
This will be visible at the following: http:///pages/facebook</p>
<p><strong>Step 3:</strong> <em>Copy the example in the jQuery folder, alter your default layout</em><br />
Reformat the example after you copy it to the ./app/views/pages/facebook.ctp to take out the header stuff. We only need the body, and make sure you put the header stuff in the default layout. Below is the code to add to the default layout:</p>
<pre>&lt;!doctype html&gt;
&lt;html xmlns:fb="http://www.facebook.com/2008/fbml" xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
    &lt;?php echo $this-&gt;Html-&gt;charset(); ?&gt;
    &lt;title&gt;
        &lt;?php __('CakePHP: the rapid development php framework:'); ?&gt;
        &lt;?php echo $title_for_layout; ?&gt;
    &lt;/title&gt;
    &lt;?php echo $this-&gt;Html-&gt;css('cake.generic'); ?&gt;
    &lt;?php echo $scripts_for_layout; ?&gt;
    &lt;style&gt;
        body {
            font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
        }
        h1 a {
            text-decoration: none;
            color: #3b5998;
        }
        h1 a:hover {
            text-decoration: underline;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;</pre>
<p>Simple code to put in to test the JS API:</p>
<pre>&lt;h1&gt;Connect JavaScript - jQuery Login Example&lt;/h1&gt;
    &lt;div&gt;
      &lt;button id="login"&gt;Login&lt;/button&gt;
      &lt;button id="logout"&gt;Logout&lt;/button&gt;
      &lt;button id="disconnect"&gt;Disconnect&lt;/button&gt;
    &lt;/div&gt;
    &lt;div id="user-info" style="display: none;"&gt;&lt;/div&gt;

    &lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&gt;&lt;/script&gt;

    &lt;div id="fb-root"&gt;&lt;/div&gt;
    &lt;script src="http://connect.facebook.net/en_US/all.js"&gt;&lt;/script&gt;
    &lt;script&gt;
      // initialize the library with the API key
      FB.init({ apiKey: 'YOUR API KEY WOULD GO HERE' });

      // fetch the status on load
      FB.getLoginStatus(handleSessionResponse);

      $('#login').bind('click', function() {
        FB.login(handleSessionResponse);
      });

      $('#logout').bind('click', function() {
        FB.logout(handleSessionResponse);
      });

      $('#disconnect').bind('click', function() {
        FB.api({ method: 'Auth.revokeAuthorization' }, function(response) {
          clearDisplay();
        });
      });

      // no user, clear display
      function clearDisplay() {
        $('#user-info').hide('fast');
      }

      // handle a session response from any of the auth related calls
      function handleSessionResponse(response) {
        // if we dont have a session, just hide the user info
        if (!response.session) {
          clearDisplay();
          return;
        }

        // if we have a session, query for the user's profile picture and name
        FB.api(
          {
            method: 'fql.query',
            query: 'SELECT name, pic FROM profile WHERE id=' + FB.getSession().uid
          },
          function(response) {
            var user = response[0];
            $('#user-info').html('&lt;img src="' + user.pic + '"&gt;&lt;br /&gt;' + user.name).show('fast');
          }
        );
      }
    &lt;/script&gt;</pre>
<p>Now, you should be able to go to your page and see a login button, a logout button, and disconnect. If you are already logged in to Facebook, you should see your own profile image (if you allowed it). If not, click the login button, and if you have the Facebook application set up correctly, and the API correct, you should be able to log in.</p>
<p><strong>Step 4:</strong> <em>Check the PHP API</em><br />
Now that we can see the JS API is working fine, we need to set up something so we can create a test for the PHP API. This is actually easier than it appears.</p>
<p>I first create a new layout just for the Facebook app, which I call facebook.ctp. It basically just erases all Cake icons, other special icons, and any kind of SQL output. Rename the title to &#8220;Facebook Testing:&#8221; (or whatever you want). Then create a new controller with no model. Use the <em>App::import</em>statement to include the facebook.php file. Now create an index function, and copy and paste the PHP area from the example included in the PHP SDK into the &#8220;index&#8221; function. It may be similar to this:</p>
<pre>&lt;?php
App::import('Vendor', 'facebook');
class FbsController extends AppController {
	var $name = 'Fbs';
	var $uses = null;	

	function index() {
	    // Set the layout
        $this-&gt;layout = 'facebook';        

        // Create our Application instance (replace this with your appId and secret).
        $facebook = new Facebook(array(
            'appId'  =&gt; 'YOUR APPLICATION ID WOULD GO HERE',
            'secret' =&gt; 'YOUR SECRET KEY HERE - DO NOT DISPLAY THIS TO ANYONE',
            'cookie' =&gt; true,
        ));

        $session = $facebook-&gt;getSession();
        $appID   = $facebook-&gt;getAppId();
        $uid     = '';
        $me = null;
        // Session based API call.
        if ( $session ) {
            try {
                $uid = $facebook-&gt;getUser();
                $me = $facebook-&gt;api('/me');
            } catch (FacebookApiException $e) {
                error_log($e);
            }
        }

        $logoutUrl = '';
        $loginUrl  = '';

        // login or logout url will be needed depending on current user state.
        if ( $me ) {
            $logoutUrl = $facebook-&gt;getLogoutUrl();
        } else {
            $loginUrl = $facebook-&gt;getLoginUrl();
        }
        // Set the variables to use in the view
        $this-&gt;set(array(
            'facebook'  =&gt; $facebook,
            'session'   =&gt; $session,
            'me'        =&gt; $me,
            'logoutUrl' =&gt; $logoutUrl,
            'loginUrl'  =&gt; $loginUrl,
            'appID'     =&gt; $appID,
            'uid'       =&gt; $uid,
        ));
	}
}
?&gt;</pre>
<p>In this controller, we are setting the basics we need for the application. Using the SDK to see if they are logged in, getting our app id, and setting the proper variables to be used later. We set the login or logout URLs to display the proper links. Finally, we are setting the variables to be used in the view.</p>
<p>Now we need to create a new directory in the views area named &#8220;fbs&#8221; and a file in that directory  named &#8220;index.ctp&#8221;. This view will house the base items for displaying the example. You can copy the HTML parts from the example.php and use them in this view. I modified it a little to alter what is shown.</p>
<pre>&lt;h2&gt;Test Facebook File&lt;/h2&gt;
&lt;div id="fb-root"&gt;&lt;/div&gt;
    &lt;script&gt;
      window.fbAsyncInit = function() {
        FB.init({
          appId   : '&lt;?php echo $appID ?&gt;',
          session : &lt;?php echo json_encode($session); ?&gt;, // don't refetch the session when PHP already has it
          status  : true, // check login status
          cookie  : true, // enable cookies to allow the server to access the session
          xfbml   : true // parse XFBML
        });

        // whenever the user logs in, we refresh the page
        FB.Event.subscribe('auth.login', function() {
          window.location.reload();
        });
      };

      (function() {
        var e = document.createElement('script');
        e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
        e.async = true;
        document.getElementById('fb-root').appendChild(e);
      }());
    &lt;/script&gt;

    &lt;?php if ($me): ?&gt;
        &lt;a href="&lt;?php echo $logoutUrl; ?&gt;"&gt;
          &lt;img src="http://static.ak.fbcdn.net/rsrc.php/z2Y31/hash/cxrz4k7j.gif"&gt;
        &lt;/a&gt;
    &lt;?php else: ?&gt;
        &lt;div&gt;
          Using JavaScript &amp; XFBML:
          &lt;br /&gt;
          &lt;fb:login-button&gt;&lt;/fb:login-button&gt;
          &lt;br /&gt;&lt;br /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          Without using JavaScript &amp; XFBML:&lt;br /&gt;
          &lt;a href="&lt;?php echo $loginUrl; ?&gt;"&gt;
            &lt;img src="http://static.ak.fbcdn.net/rsrc.php/zB6N8/hash/4li2k73z.gif"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
    &lt;?php endif ?&gt;

    &lt;h3&gt;Session&lt;/h3&gt;
    &lt;?php if ($me): ?&gt;
        &lt;pre&gt;&lt;?php
        echo "session.base_domain - " .
            $session['base_domain'] . "&lt;br /&gt;session.expires - " .
            $session['expires'] . "&lt;br /&gt;";
        ?&gt;&lt;/pre&gt;

        &lt;h3&gt;You&lt;/h3&gt;
        &lt;img src="https://graph.facebook.com/&lt;?php echo $uid; ?&gt;/picture?type=large" /&gt;
        &lt;br /&gt;
        &lt;?php echo $me['name']; ?&gt;

        &lt;h3&gt;Your User Object&lt;/h3&gt;
        &lt;pre&gt;&lt;?php print_r($me); ?&gt;&lt;/pre&gt;
    &lt;?php else: ?&gt;
        &lt;strong&gt;&lt;em&gt;You are not Connected.&lt;/em&gt;&lt;/strong&gt;
    &lt;?php endif ?&gt;</pre>
<p>The login button examples will show two different ways to authenticate the user to the application. The JS example will open a new window and will log them in, post it back to the page and show the login. The non JS example will take the user to the page and give them the Facebook page to log in. Once they log in, it then redirects back to the calling page.</p>
<p>The information on the page shows the Session info, and below that, it shows a large profile image of the person logged in, and then an array of data of all public accessible information. If the person has set their profile to be private, then it will not show a lot of information.</p>
<p>These two examples can be seen at the following, with screenshots below:<br />
The JS SDK example<br />
<a href="http://www.stephenhird.com/pages/facebook">http://www.stephenhird.com/pages/facebook</a></p>
<div style="width:80%; margin-left:auto; margin-right:auto; display:inline-block;">
<div id="attachment_408" class="wp-caption alignleft" style="width: 160px"><a href="http://www.hirdweb.com/wp-content/uploads/2010/10/js_sdk1.jpg"><img class="size-thumbnail wp-image-408" title="JavaScript SDK prelogin" src="http://www.hirdweb.com/wp-content/uploads/2010/10/js_sdk1-150x150.jpg" alt="JavaScript SDK prelogin" width="150" height="150" /></a><p class="wp-caption-text">JavaScript SDK prelogin</p></div>   <div id="attachment_409" class="wp-caption alignright" style="width: 160px"><a href="http://www.hirdweb.com/wp-content/uploads/2010/10/js_sdk2.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2010/10/js_sdk2-150x150.jpg" alt="JavaScript SDK post-login" title="JavaScript SDK post-login" width="150" height="150" class="size-thumbnail wp-image-409" /></a><p class="wp-caption-text">JavaScript SDK post-login</p></div></div>
<p>The PHP SDK example<br />
<a href="http://www.stephenhird.com/fbs/">http://www.stephenhird.com/fbs/</a></p>
<div style="width:80%; margin-left:auto; margin-right:auto; display:inline-block;">
<div id="attachment_420" class="wp-caption alignleft" style="width: 160px"><a href="http://www.hirdweb.com/wp-content/uploads/2010/10/php_sdk1.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2010/10/php_sdk1-150x150.jpg" alt="PHP SDK example pre login" title="PHP SDK example pre login" width="150" height="150" class="size-thumbnail wp-image-420" /></a><p class="wp-caption-text">PHP SDK example pre login</p></div>    <div id="attachment_421" class="wp-caption alignright" style="width: 160px"><a href="http://www.hirdweb.com/wp-content/uploads/2010/10/php_sdk2.jpg"><img src="http://www.hirdweb.com/wp-content/uploads/2010/10/php_sdk2-150x150.jpg" alt="PHP SDK example post login" title="PHP SDK example post login" width="150" height="150" class="size-thumbnail wp-image-421" /></a><p class="wp-caption-text">PHP SDK example post login</p></div>
</div>
<p>In the next post, we will work on getting a good discussion going, which is really, really simple, we will tie it to a DB saved question that can be entered thru an admin form, and then we will examine the moderation of the Facebook comments on the discussion.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2010/10/17/facebook-application-on-the-site/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Facebook Integration Initial Steps</title>
		<link>http://www.hirdweb.com/2010/09/22/facebook-integration-initial-steps/</link>
		<comments>http://www.hirdweb.com/2010/09/22/facebook-integration-initial-steps/#comments</comments>
		<pubDate>Wed, 22 Sep 2010 13:39:46 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[Facebook]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=377</guid>
		<description><![CDATA[The next few posts will examine how I will integrate a Facebook application into one of my sites: stephenhird.com. If you have browsed to this site, you will see there is already a Facebook platform there. And while it does show some of the great things to do with the API, it is very basic [...]]]></description>
			<content:encoded><![CDATA[<p>The next few posts will examine how I will integrate a Facebook application into one of my sites: stephenhird.com. If you have browsed to this site, you will see there is already a Facebook platform there. And while it does show some of the great things to do with the API, it is very basic and really does nothing. So In this post I will examine what I want to do with it, how to set up an initial application on Facebook, and then go from there. This will likely happen over the next few weeks/months and will be a little more drawn out depending on how much time I have to document this process. This is step one, and step one will always include planning. </p>
<p>First I need to figure out what I want to have on my site. Since it is my name that is on the URL, I will need to create a way that this site will be an online portfolio, biography and information repository. When doing this for any site, it is important to remember your brand. Even for individual sites like this, it is important for branding, because this is who I am, I do not want it to be a classic case of slop on the web. This site before was mainly just a testing ground and now will need to be more. </p>
<p>A side note here, my name is the same spelling as a famous photographer based in London. His site is well put together with good descriptive links and a great example of combining minimalist ideas with styled presentation. He keeps his brand on the pages and the site does not confuse or mislead with extra peripheral items, or overuse of Flash or other heavy web technologies loading down the page. His site is located at: <a href='http://www.stephenhird.co.uk/' target='_blank'>http://www.stephenhird.co.uk/</a></p>
<p>OK, now on to the planning and setting up the Facebook Application.<br />
<span id="more-377"></span><br />
 First I will list out my goals with the site, and why put it in a Facebook application. I am choosing to do this myself, however, these may not really warrant a full blown Facebook application with every site/web application. </p>
<p>1. Describe who I am<br />
2. Display samples of my work, all work: code, art, etc<br />
3. Provide a theme/question for comments and research<br />
4. Provide contact information<br />
5. Online Resume with ability for download (never hurts to have a resume handy)<br />
6. Share my images from different travels and experiences<br />
7. A place where I can see where I have been, where I am going and how to get there</p>
<p>This is the base ideas behind my site. This is for a more personal reflection tying into the other interests in my life. </p>
<p>I am planning on building the base in CakePHP, and adding the Facebook API in to the application. Which means this will also generate more posts on CakePHP application development. The back end will be a DB driven application, with all frontend interaction via Facebook login. All Admin functionality of the site will be outside of the Facebook authentication. </p>
<p>Now since I already have the site up, I need to create the application side in Facebook.</p>
<p>1. Log in to Facebook, or go to this URL: <a href='http://www.facebook.com/developers/apps.php' target='_blank'>http://www.facebook.com/developers/apps.php</a><br />
2. New policies for Facebook insist that you either enter a mobile number or a credit card to validate you are a real person. The phone number is what I would suggest to use.<br />
3. After you enter the confirmation code, return to the main window to enter the name of the application and agree to terms<br />
4. Put in the security check code<br />
5. This will take you to the first part of the form</p>
<p>Now the form, it is a quick and easy form to fill out. There are four tabs and I will highlight each and a few items on each that I usually do:</p>
<ol>
<li>About
<ul>
<li>Enter the application name, upload images, icons, etc. </li>
<li>Enter the support email or page, and put in a privacy page, very important if you want to have this application take off.</li>
</ul>
</li>
<li>Web Site
<ul>
<li>Site url would be the site you are creating to work with the API</li>
<li>Domain is important if you want subdomains (for things like mobile domains, etc) to be authenticated using the same key</li>
</ul>
</li>
<li>Facebook Integration
<ul>
<li>Canvas will determine how you want the page top appear inside Facebook.</li>
<li>This is also where you decide to do a FBML (their markup) or an iFrame. I usually choose iFrame mainly because in my applications the bulk of interactivity is done on the domain and not Facebook. However, you can always do FBML to keep everything in Facebook. </li>
<li>Profile tabs will enable new tabs on the app page that can be added/used on individual profile pages</li>
</ul>
</li>
<li>Mobile and Devices
<ul>
<li>If you are developing for mobile users, enter the type of core markup to use, and the id for the iTunes app store. Important to <a href="http://developers.facebook.com/docs/guides/mobile/#itunes_app_store_id" target="_blank">read here</a> if you plan on making a mobile app</li>
</ul>
</li>
<li>Advanced>
<ul>
<li>Each section in this page holds a very important piece that may affect your application.</li>
<li>Authentication holds the sandbox keys, and where Facebook looks if a user takes your app off</li>
<li>Migrations holds the areas for callbacks, empty strings and OAuth</li>
<li>Security will allow you to enable only a few select IP addresses to access your application on the Facebook platform</li>
<li>Advertising and Advanced can do different things for ads, preloading, etc.</li>
</ul>
</ol>
<p>Above all else, read the side information if you get stuck, that is why it is there. </p>
<p>Once you are done, click the &#8220;Save Changes&#8221; button and it will save your data and create your application. </p>
<p>There, you have now just created a shell application on the Facebook platform and ready to enable the client side application. </p>
<p>In order to submit this application, Facebook posts this:<br />
&#8220;Your application must have at least 5 total users or 10 monthly active users before you can submit it to the Application Directory. We cannot showcase any applications that are under construction or do not utilize the Facebook Platform.&#8221;</p>
<p>So do not expect to create an application and then suddenly have it spread like wildfire. It has to provide useful or entertaining &#8220;stuff&#8221;, it has to work, and it has to be used. Once it is, then it can be submitted. So we have accomplished step one of the integration. Next up is to get the API on the site, and start developing the site. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2010/09/22/facebook-integration-initial-steps/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PEAR and CakePHP</title>
		<link>http://www.hirdweb.com/2009/05/04/pear-and-cakephp/</link>
		<comments>http://www.hirdweb.com/2009/05/04/pear-and-cakephp/#comments</comments>
		<pubDate>Tue, 05 May 2009 02:23:41 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[PEAR]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=185</guid>
		<description><![CDATA[This post is about my experience with loading in PEAR to a CakePHP 1.2.x application. This may be the right way or the wrong way, but I got it to work throughout the application. I had to do some changes, and if there is a better way of doing this, please let me know. First [...]]]></description>
			<content:encoded><![CDATA[<p>This post is about my experience with loading in PEAR to a CakePHP 1.2.x application. This may be the right way or the wrong way, but I got it to work throughout the application. I had to do some changes, and if there is a better way of doing this, please let me know. </p>
<p>First off, here is the issue. I needed to be able to export a group of records from the database to an excel spreadsheet. I have tried to use the <a href="http://bakery.cakephp.org/articles/view/excel-xls-helper" target="_blank">Excel Spreadsheet add in that is listed on the Bakery</a>. It works nice, and I had to do some modification for 1.2, but it worked. But not the way I wanted it. I have used the PEAR library Spreadsheet_Excel_Writer before and I like the type of control that I wanted, over the cells, the formatting, the merging, etc etc etc. It provides the type of control that I wanted. So here is what I did to get this to work with the CakePHP framework. </p>
<p>First, I have to download the PEAR library and the Spreadsheet_Excel_Writer libraries to use. Since I use a local system to help develop, I could download these libraries to the local system and transport these over to the CakePHP application. So I went to PEAR site to get the libraries. To download these I ran the following commands:</p>
<pre>
pear install PEAR-1.8.1
pear install OLE-1.0.0RC1
pear install Spreadsheet_Excel_Writer-0.9.1
</pre>
<p>URL&#8217;s are listed below:</p>
<p>http://pear.php.net/package/PEAR/download</p>
<p>http://pear.php.net/package/Spreadsheet_Excel_Writer/download</p>
<p>http://pear.php.net/package/OLE/download</p>
<p><span id="more-185"></span><br />
These downloaded the to local drives and I copied them over to the CakePHP area. Here is where it gets a little tricky. And I thank &#8220;brian&#8221; who helped me on the <a href="http://groups.google.com/group/cake-php">Cake Google group</a> to get past this when I got into a problem. So here we go, diving in to this. </p>
<p>First off, the PEAR libraries need to be put in the vendors directory. If you look at the directory structure for Cake, it appears like this:<br />
/app<br />
/cake<br />
/vendors</p>
<p>Inside of the /app directory, there is another vendors directory. This is where I put the PEAR libraries. This causes problems, because in the /cake/config/paths.php file, there is a path defined for VENDORS, and for PEAR:</p>
<pre>
if (!defined('VENDORS')) {
	define('VENDORS', CAKE_CORE_INCLUDE_PATH.DS.'vendors'.DS);
}

define('PEAR', VENDORS.'Pear'.DS);
</pre>
<p>Now, I have put the PEAR libraries in the top level vendor directory. If you choose to put it in the app/vendors directory, then you may need to change a core file, which is not advisable, because you would need to change the path above to</p>
<pre>
if (!defined('VENDORS')) {
	define('VENDORS', CAKE_CORE_INCLUDE_PATH.DS.'app/vendors'.DS);
}
</pre>
<p>So back to the PEAR libraries. Here is what I needed to do. I moved the PEAR directories to the /vendors directory. So here is what that directory looks like:</p>
<pre>
/vendors
    /css
    /js
   /Pear   <--- Look at this, case sensitive based on those paths above
        /OLE
        /OS
        /PEAR
        /scripts
        /Spreadsheet
        INSTALL
        LICENSE
        OLE.php
        package.dtd
        PEAR.php
        PEAR5.php
        README
        System.php
        template.spec
    /shells
</pre>
<p>This is part of the PEAR install we need to do. Now we need to update the paths in some of these files so that CakePHP can find them and include them. </p>
<p>**** ORIGINAL ENTRY THAT HAS BEEN EDITED/DEPRECATED ********************<br />
**** EDIT: Based on the original post this is the method I originally used. This way works, but requires a<br />
**** little too much overhead and editing the files in the PEAR libraries which is never really a good idea<br />
**** and should only be used sparingly. To see the way that it should be done, please see below this<br />
**** section.<br />
****<br />
**** In the file /vendors/Pear/Spreadsheet/Excel/Writer.php there is 2 requires<br />
**** require_once 'PEAR.php';<br />
**** require_once 'Spreadsheet/Excel/Writer/Workbook.php';<br />
****<br />
**** In order for the Cake App to see these, at least in my set up, I needed to change these to the<br />
**** following:<br />
**** require_once 'PEAR.php';<br />
**** require_once PEAR . 'Spreadsheet/Excel/Writer/Workbook.php';<br />
****<br />
**** I needed to add "PEAR . " to the require_once call. Now I needed to add this to the following files:<br />
**** /vendors/Pear/Spreadsheet/Excel/Writer.php<br />
**** require_once 'PEAR.php';<br />
**** require_once PEAR . 'Spreadsheet/Excel/Writer/Workbook.php';<br />
****<br />
**** /vendors/Pear/Spreadsheet/Excel/Writer/Workbook.php<br />
**** require_once PEAR . 'Spreadsheet/Excel/Writer/Format.php';<br />
**** require_once PEAR . 'Spreadsheet/Excel/Writer/BIFFwriter.php';<br />
**** require_once PEAR . 'Spreadsheet/Excel/Writer/Worksheet.php';<br />
**** require_once PEAR . 'Spreadsheet/Excel/Writer/Parser.php';<br />
**** require_once PEAR . 'OLE/PPS/Root.php';<br />
**** require_once PEAR . 'OLE/PPS/File.php';<br />
****<br />
**** /vendors/Pear/OLE/PPS.php<br />
**** require_once 'PEAR.php';<br />
**** require_once PEAR . 'OLE.php';<br />
****<br />
**** This had helped the application find my PEAR libraries when trying to do this<br />
**** END ORIGINAL ENTRY ***********************************************</p>
<p><strong><em>Now, a word about the edit. The above method works, but is not a preferred method. The best method for this, so there is no need to edit PEAR library files is the following. And a big thanks to <a href="http://cakebaker.42dh.com/">Daniel Hofstetter</a> for pointing this out. </em></strong></p>
<p>I put this at the top of my controller file. I am sure there is better places for this, probably even the app_controller file so that all controllers get the needed include path set. Here is what I did. </p>
<p>I needed to append the include path so that the new PEAR path would be found. After the php opening, I added this line:</p>
<pre>
ini_set("include_path", PEAR . PATH_SEPARATOR . ini_get("include_path"));
</pre>
<p>I am going to go over this just a little. First off, the ini_set is called to set the include_path. But we do not want to destroy any other include paths that are set up as well. So when we add the PEAR path, we need to also include the other paths as well. So the initial include_path was as follows (given in example form only, where the directory "test" is where I have CakePHP installed)<br />
ini_get("include_path") = </p>
<pre>
/www/htdocs/html/test:/www/htdocs/html/test/app/:.:/usr/local/php5/lib/php
</pre>
<p>Since CakePHP already defines the variable for the PEAR path as "PEAR", we can use that to add to the include path, like shown above. After setting the path using ini_set(), we run ini_get("include_path") it would = </p>
<pre>
/www/htdocs/html/test/vendors/Pear/:/www/htdocs/html/test:/www/htdocs/html/test/app/:.:/usr/local/php5/lib/php
</pre>
<p>By doing it this way, there is no need to edit the PEAR library files, and we can add new PEAR libraries without having to worry about editing those files as well. </p>
<p>Now, I needed to make the controller aware of the vendor library. In my controller file I added this line before the class declaration:</p>
<pre>
App::import('vendor', 'Spreadsheet_Excel_Writer', array('file' => '../vendors/Pear/Spreadsheet/Excel/Writer.php'));
</pre>
<p>In Cake 1.2, this is how the vendor's are imported. The vendor() declaration has been deprecated. This imports a vendor, gives the class a name (I choose the base one that it is usually called), and the location of the of the file. In my set up, I needed to add the "../", you may not have to. </p>
<p>In the function, (I called "export"), I did not want to have a "view" page for it. The first thing I did was grab the information I needed. For this example, I needed all users that signed up for a conference. So I grabbed that information and put it in an array $registrations. </p>
<pre>
function export ($id = null){
       // I only want to get a specific conference, not all of them
	if ( $id == 'all' ){
		$this->Session->setFlash('Please select a specific conference to export the registrations.');
		$this->redirect(array('action' => 'index'));
	}

	// Now get the registrations for the conference
	$registrations = $this->Registration->find('all',
		array(
			'conditions' => array('conference_id' => $id),
			'fields' => array('*'),
			'recursive' => '-1',
			'order' => array('Registration.created'),
		)
	);
</pre>
<p>Now comes the fun part, building the column heading array, and then instantiating the writer</p>
<pre>
	// Set up the header array
	$titles = array(
		'Name' => 15,
		'Address' => 20,
		'City' => 20,
		'State' => 7,
		'Zip Code' => 10,
		'Email' => 20,
		'Phone' => 13,
	);

	$rn = 0; // row number
	// Build the XLS file using PEAR
	$xlsBook = new Spreadsheet_Excel_Writer();
	$xlsBook->send("registrations.xls");
	$xls =&#038; $xlsBook->addWorksheet('Registrations');
</pre>
<p>Everything else is now just as the same as it would be with the Spreadsheet-Excel_writer. Create the formats as you would like, for text, numerics, specialized strings, colors, etc. Write the sheet headings, if you so desire</p>
<pre>
	/* Create styles for the spreadsheet */
	$format_bold =&#038; $xlsBook->addFormat();
	$format_bold->setBold();

	$main =&#038; $xlsBook->addFormat(
		array('Size' => 14,
			'Align' => 'center',
			'Color' => 'black',
			'Bold' => 'true'
		));
	$main->setBold();

	$formatText =&#038; $xlsBook->addFormat(array('Size' => 11));

	$cn = 0;
	$xls->write($rn, 0, "CONFERENCE REGISTRATIONS", $main);
	$xls->mergeCells($rn,0,$rn,11);
	$rn++;
</pre>
<p>As you can see, just use the writer calls to write the data, format it, and do what you need. To get more information on this, please <a href="http://pear.php.net/package/Spreadsheet_Excel_Writer/docs">check the PEAR documentation for this library</a>. </p>
<p>Finish up the column headings by doing a quick little loop</p>
<pre>
	// Set up the headings of the columns
	foreach ( $titles as $t => $val){
		$xls->setColumn($cn, $cn, $val);
		$xls->write($rn, $cn++, $t, $format_bold);
	}
	$rn++;
	// reset the column num
	$cn = 0;
</pre>
<p>Now you can do the actual rows in a loop:</p>
<pre>
	foreach ( $registrations as $r ){
		$xls->write($rn, $cn++, $r['Registration']['name'], $formatText);
	    $xls->write($rn, $cn++, $r['Registration']['address'], $formatText);
	    $xls->write($rn, $cn++, $r['Registration']['city'], $formatText);
	    $xls->write($rn, $cn++, $r['Registration']['state'], $formatText);
	    $xls->write($rn, $cn++, $r['Registration']['zip_code'], $formatText);
	    $xls->write($rn, $cn++, $r['Registration']['email'], $formatText);
	    $xls->write($rn, $cn++, $r['Registration']['contact_phone1'], $formatText);
	    // cycle to the next row
	    $rn++;
	    // Reset the column
	    $cn = 0;
	}

	$xlsBook->close();
	exit();
} // end of function
</pre>
<p>Now, this does the work for me on my code. To call it in the view, I have a page that shows all registrations on the page, the function name is "registrations". I set a variable in this function for the ID number to be passed to the view. In the view for this function, I have put the following:</p>
<pre>
if ( $param != 'all') {
	echo "&lt;p&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - - &lt;b&gt;";
	echo $html->link(__('EXPORT DATA', true), array('action' => 'export', $param) );
	echo "&lt;/b&gt; - - &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;";
}
</pre>
<p>So I want a specific conference. If there is not one, and they are viewing all registrations for all conferences, then it does not show the link. But if it is a specific id, then it shows the link to export with the corresponding parameter for the ID. </p>
<p>And there it is. Using the Spreadsheet_Excel_Writer PEAR library with CakePHP 1.2. </p>
<p>Again, this works for me, and there may be a better way of doing things, and if so, please feel free to tell me. I am always looking for new things to learn. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2009/05/04/pear-and-cakephp/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Regular Expressions</title>
		<link>http://www.hirdweb.com/2009/04/05/regular-expressions/</link>
		<comments>http://www.hirdweb.com/2009/04/05/regular-expressions/#comments</comments>
		<pubDate>Mon, 06 Apr 2009 01:55:06 +0000</pubDate>
		<dc:creator>stephen</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[cakePHP]]></category>
		<category><![CDATA[regex]]></category>

		<guid isPermaLink="false">http://www.hirdweb.com/?p=168</guid>
		<description><![CDATA[Here is a topic that has really flustered a lot of developers. Regular expressions is a concept that can be hard to get a real handle on. PHP has a couple of functions that can help do regular expressions. The one I focus on most is using the function: preg_match() This is a very useful [...]]]></description>
			<content:encoded><![CDATA[<p>Here is a topic that has really flustered a lot of developers. Regular expressions is a concept that can be hard to get a real handle on. PHP has a couple of functions that can help do regular expressions. The one I focus on most is using the function:<br />
preg_match()</p>
<p>This is a very useful tool, and if you look at the <a href="http://us3.php.net/manual/en/function.ereg.php">PHP manual for ereg()</a>, it states that the function &#8220;preg_match&#8221; is a faster alternative to &#8220;ereg()&#8221;. Now while I am not going to get into the details of the speed and response times for both functions, as there will always be someone with a different opinion or case that shows how their way is better, and that is fine. What most people have a hard time dealing with is getting the actual match to do what is needed. There are times when It is just easier to do a Google search and get some code that someone else has already done and plug it in. But the real power is knowing what you are doing first, that way you can build your own.</p>
<p>For this example, we can take a look at CakePHP&#8217;s own little validation object. When you set up a model and add some validation to it, it calls this object. Based on the data that this going into the tables, it will call one of these functions. The way these functions work is by checking the input for a specific character list/set that should be contained in the text. If the entry does not match up, then it is not validated. The way CakePHp does this is by using the preg_match() function.<br />
<span id="more-168"></span></p>
<p>If you are new to regular expressions, then seeing something like this:</p>
<pre>
define('VALID_EMAIL', "/^[a-z0-9!#$%&#038;'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[a-z]{2,4}|museum|travel)$/i");
</pre>
<p>may be a little scary. But never fear, this is not as bad as it seems. It just looks real scary. And besides, even Cake made it a little better. </p>
<p>So let&#8217;s look at this function:</p>
<pre>
function email($check, $deep = false, $regex = null) {
. . .
	if (is_null($_this->regex)) {
		$_this->regex = '/^[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+)*@' . $_this->__pattern['hostname'] . '$/i';
	}
	$return = $_this->_check();
. . .
}

function _check() {
. . .
	if (preg_match($_this->regex, $_this->check)) {
		$_this->error[] = false;
		return true;
	} else {
		$_this->error[] = true;
		return false;
	}
}
</pre>
<p><em>If you would like to know more about the function, then please browse to the CakePHP manual to get more info about that function, since all this is going to do is point out the regex part of it. </em></p>
<p>First off, the function called is &#8220;email&#8221;. In this function, there is a regex match set that is the following:<br />
&#8216;/^[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+)*@&#8217; . $_this->__pattern['hostname'] . &#8216;$/i&#8217;</p>
<p>It passes that to the _check() function, which puts this regex pattern to the email address that is entered to see if there is a match. So to jump ahead if someone were to put in an email address of:<br />
 &#8211; flavio@nothing.com<br />
It would pass that function. (NOTE: this does not mean it is a valid email address that can recieve emails, it just means that the characters in the email address are in the valid format of (address_name)@(hostname).(Extension) )</p>
<p>But this also means that someone could put in the email address:<br />
 &#8211; flavio@nothing.show<br />
And that would pass as well, while the following email addresses:<br />
 &#8211; flavio<br />
 &#8211; flavio@nothing<br />
 &#8211; flavioATnothingDOTcom<br />
would fail. </p>
<p>But now that we know that, how did we get there? </p>
<p>Let&#8217;s break down the match. </p>
<p>The first thing, we are looking for a pattern, so we need to ad the following:<br />
/ /<br />
around the pattern. This is a Perl syntax that is followed for finding patterns. Now the bookends come, with the caret ( ^) and the dollar sign ( $ ). The caret means to search the beginning of the string for the pattern, and the dollar sign matches the end of the string. In this example, the caret matches the first part of the email address entered, and the dollar sign matches the end. </p>
<pre>
'/^[ ]$/'
</pre>
<p>So we are off to a good start. but there since we are looking for a pattern that does not need to be case sensitive, as we do not care if there are uppercase letters or not, we need to add an &#8220;i&#8221; at the end. </p>
<pre>
'/^[ ]$/i'
</pre>
<p>Now we are ready to start looking at the beginning of the string. We are going to put this in a bracket to group the characters, or create a class. We want to get any valid characters for an email address. This would include any letters, numbers and some special characters. We will need to escape some of these (using the &#8220;\&#8221; to escape)</p>
<pre>
'/^[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]$/i'
</pre>
<p>This will now look for the address name, sort of. As long as there are no periods in the address account name, it will work, but there are addresses out there that have a &#8220;.&#8221; in it:<br />
 &#8211; flavio.elguappo@nothing.com<br />
would not pass the validation as of yet (given that we would have also added the domain by the time it is checked). </p>
<p>So we need to add that match set in to the expression. CakePHP does this by using an atomic grouping. Using the parenthesis usually means that a &#8220;backreference&#8221; should be done. You can escape this by using &#8220;?:&#8221;. The question mark-colon combo after the first parenthesis signifies that what is coming is not a backreference. Now, to get the addresses that have a period in the account name we add this:<br />
(?:\.[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+)</p>
<p>As you can see, this is almost the same pattern as before. We want to check the same things again after any appearance of a period. So now it looks like:</p>
<pre>
'/^[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+)$/i'
</pre>
<p>Last things here, we need to account for the @ symbol in the address, and add the domain name. Since CakePHP already takes care of that, they have there own addition:</p>
<pre>
'/^[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+)*@' . $_this->__pattern['hostname'] . '$/i'
</pre>
<p>And the final php code would be as follows:</p>
<pre>
$checked = preg_match('/^[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;\'*+\/=?^_`{|}~-]+)*@' . $_this->__pattern['hostname'] . '$/i', $email_address_to_check);

return $checked;
</pre>
<p>Now I am sure I glossed over some regex rules and explanations. I will never claim to be an expert on regex, as I am still learning as much as I can about this. Now this is not the only way to check an email address. If you do not use CakePHP and the real nifty built in helper, then you can use this one:</p>
<pre>
"/^[^0-9][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}$/"
</pre>
<p>This one is similar, check the account name for valid characters, see if it has any periods in the name, check the @ symbol and then check the domain name, and then the domain extension, allowing for 2-4 characters in the extension. </p>
<p>And if you want to get some real good info on other regex info, here are a couple of links I found useful:<br />
<a href="http://www.webcheatsheet.com/php/regular_expressions.php">http://www.webcheatsheet.com/php/regular_expressions.php</a><br />
<a href="http://www.regular-expressions.info/tutorial.html">http://www.regular-expressions.info/tutorial.html</a><br />
<a href="http://us3.php.net/manual/en/function.preg-match.php">http://us3.php.net/manual/en/function.preg-match.php</a><br />
(Make sure you read the comments as they have some good info)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hirdweb.com/2009/04/05/regular-expressions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

