This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews. Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book. Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
First published: August 2011
Production Reference: 1180811
Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK
Credits Author Jason D. Straughan Reviewers Geert De Deckere Paul Liversidge Acquisition Editor Tarun Singh Development Editor Chris Rodrigues Technical Editor Ajay Shanker Indexer Hemangini Bari
Project Coordinator Michelle Quadros Proofreader Clyde Jenkins Graphics Nilesh Mohite Production Coordinator Adline Swetha Jesuthas Cover Work Adline Swetha Jesuthas
About the Author Jason D. Straughan lives in the beautiful Texas Hill Country, where he builds web applications, writes, and spends time with his family. He began programming in grade school, developing professionally in the early 1990s, and has spent his career working in project management and software development. His passion for open source technologies and the Web experience has been a driving force in his appreciation for PHP and the Kohana framework. Jason spends his days as a software engineer at Live Oak 360, a custom application development company focused on complex solutions and unique social platforms. During his time at Live Oak 360 and its sister company, 44Doors, Jason has assisted with the development of two software-as-a-service products, BudURL and BearHug, in addition to several custom application platforms. He also provides consultation and training to a select number of private clients, and is a founding partner in Straughan Photography (SanAntonioWeddingPhotography.com) with his wife and managing partner, Chrystina. You can follow Jason's blog, and read more about Kohana and web-related topics at www.JDStraughan.com. Most of all, I would like to think my wife, Chrystina, and my son, Michael, for all their love and support. This book would never have become a reality had it not been for their patience and understanding during many evenings and weekends of writing. I would also like to thank: Dr. Ken Jones, not only for convincing me to use PHP in the first place, but also for always offering inspiration and wise counsel; James Zimmermann, who has provided constant encouragement and optimism during this endeavor; and David Salazar, who is always willing to talk about code, and for introducing me to Kohana many years ago. Throughout the course of the writing, Isaac Castillo acted as an enthusiastic and helpful tester, sometimes without even knowing it—thanks Isaac.
The technical reviewers for this book have provided many valuable insights, enhancements, and suggestions that are reflected in the final draft. This group of reviewers had a significant impact on the final text, and I genuinely thank each and every one who contributed. Specifically, I would like to thank Geert De Deckere for his level of dedication to the project, and for being a great resource throughout the revision process. Last, but not least, I congratulate the Kohana development team and community for consistently producing an incredible framework. Without these dedicated and outstanding developers, there wouldn't be a Kohana framework for us to use, much less write about.
About the Reviewers Geert De Deckere is a web designer and developer living in Belgium. Mostly in the earlier years of Kohana, he was involved in the development of the framework. Also, he is the creator of http://kohanajobs.com/. When he is not building websites or playing around with regular expressions, he enjoys cycling. His personal website can be found at http:// geertdedeckere.be/.
Paul Liversidge is a freelance ICT management consultant by day, and a web developer by night. As a consultant, his role is to turnaround ailing ICT departments through the innovative use of technology, adopting best practice processes, and addressing the most critical assets—the people. With a technical background in network and server design, project management expertise and business acumen gained from multiple business sectors, he brings a formidable breadth of experience to bear on any ICT issue. As a web developer, he's excited by all things shiny, such as jQuery, Kohana, and pushing PHP, MySQL, CSS, and HTML forward. A frequent contributor to open source products that struggles to balance the responsibilities of a wife, two kids, and a dog, with a mania for writing code. Paul is originally from Yorkshire in the UK, but now calls Vancouver, BC, Canada home. I'd like to thank my wife and kids for allowing me to steal some time from them to review this book and my poor despondent dog that lost out on his long walks in the park. I'd also like to say a big thank you to Woody Gilk (Shadowhand) for sharing Kohana with the wider community, and I hope that my own involvement in reviewing this book has helped to widen the usage of Kohana.
www.PacktPub.com Support files, eBooks, discount offers and more You might want to visit www.PacktPub.com for support files and downloads related to your book. Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details. At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.
http://PacktLib.PacktPub.com
Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library. Here, you can access, read and search across Packt's entire library of books.
Why Subscribe?
Fully searchable across every book published by Packt
Copy & paste, print and bookmark content
On demand and accessible via web browser
Free Access for Packt account holders If you have an account with Packt at www.PacktPub.com, you can use this to access PacktLib today and view nine entirely free books. Simply use your login credentials for immediate access.
Table of Contents Preface 1 Chapter 1: Getting Started 11 System requirements Case Study Downloading and installing from the Web Time for action – Downloading from web Installing Kohana from GitHub Time for action – downloading using Git Advanced installation with Git Taking inventory Preparing the Filesystem Testing the environment Configuring Kohana for Development Environment Time for action – Setting your Timezone Configuring URL settings Time for action – setting base URL Setting up the Application Environment Time for action – Setting Kohana::$environment Enabling modules Time for action – Enabling the User Guide Creating a default config file Server configuration Configuring Apache Configuring nginx
Chapter 2: Using Views and Controllers What are controllers and views? Anatomy of a view Anatomy of a controller Updating our Welcome Controller Time for action – Getting our controller action to use a view file Passing data to the view Using the factory( ) method Time for action – Passing data via the factory method Using view variables Time for action – Passing data as view variables Using the set( ) method Time for action – Passing data using the set() method Using the bind( ) method Time for action – Passing data using the bind( ) method Putting it all together Time for action – Passing data using multiple methods Using the Template Controller Time for action – Extending Kohana's Template Controller Examining the Template controller Setting global view variables Time for action – Setting global view variables Binding global view variables Time for action – Binding global view variables Beyond the Welcome Controller Creating new controllers Time for action – Creating our first controller Time for action – Expanding on our first controller Creating a base controller Time for action – Creating an abstract controller Putting it all together Time for action – Adding stylesheets to template Adding structure to view files Time for action – Adding header and footer view files Summary
Chapter 3: Routing and Request Handling Hierarchy is King in Kohana Cascading filesystem Request flow in Kohana Using the Request object
Creating a profile page to render messages Time for action – Creating a profile page with messages Calling a new request inside a request Time for action – Creating a request within a request Routing in Kohana Time for action – Making profiles dynamic using ID Creating friendly URLs using custom routes Time for action – Creating a Custom Route Making routes explicit Time for action – Adding regex to a route Allowing additional data in a URI Time for action – Allowing additional segments in a route Using subdirectories with routes Time for action – Using Subdirectories and Routes Summary
Chapter 4: Working with Helpers and Classes Autoloading classes Introducing helpers The Date class The HTML Class
76 77 79 79 83 84 86 86 88 88 89 89 90 91 93
95 96 96 97 98
The HTML::chars() method The HTML::obfuscate() method The HTML::anchor() method The HTML::image() method The HTML::mailto() method
99 99 100 101 101
The Inflector Class The URL class
101 103
The URL::title() method The URL::base() method The URL::query() method
103 103 104
The Arr (Array) Class
104
The Arr::get() method The Arr::extract() method The Arr::pluck() method The Arr::merge() method The Arr::overwrite() method The Arr::flatten() method The Arr::unshift() method The Arr::map() method The Arr::is_array() method The Arr::range() method
105 105 105 106 107 108 109 109 110 110
The Cookie class The Encrypt (Encryption) Class Time for action – Creating configuration for Encryption [ iii ]
111 112 113
Table of Contents
The Feed Class The Form Class Time for action – Creating a login form using the Form helper class Time for action – Creating a signup form for new users Extending Helpers Transparent Class Extension Time for action – Extending the HTML helper method Creating your own helpers Summary
Chapter 5: Installing and Configuring Modules Anatomy of a Module Loading and configuring modules Bundled modules User guide module Image module oAuth module Database and ORM modules Time for action – Configuring your application to use a database Time for action – Creating a database table and using the query builder Pagination module Time for action – Adding pagination to database results Auth module Code Bench module Cache and Unit Test modules Installing modules Installing modules from source files Time for action – Installing a module from an archive Installing modules via Git Time for action – Installing a module using git Creating your own modules Summary
Chapter 6: Interacting with the Database Anatomy of a model Creating models Using the Database module Configuring the Database module Creating and running queries Using the Query Builder
Time for action – Refactoring Model_Message to allow dynamic search Insert statements
179 185
Time for action – Adding the ability to insert messages in case study site Updating statements
185 192
Time for action – Add ability to edit messages in case study site Delete statements
193 201
Time for action – Add ability to delete messages from the case study site Other functions and expressions
202 209
Using the Database Module with sessions Configuring database sessions Time for action – Adding session handling with the database adapter Using Database Sessions Summary
Chapter 7: Using the ORM and Auth Modules Using the ORM module Setting up the ORM module
211 211 211 212 213
215 215 216
Convention over configuration ORM API Overview
216 217
Time for action – Updating Message Model to use ORM ORM, Forms, and Validation The Validation class
224 230 230
Time for action – Adding validation to Message Model ORM and Relational Data Using the Auth module Configuring the Auth module Time for action – Adding users and Auth to case study site Implementing the Auth module Time for action – Adding the Auth functionality to the case study site Securing controllers and actions Time for action – Adding secure actions to the case study site Summary
Chapter 8: Troubleshooting and Error Handling Debugging code with Kohana Debugging with the Kohana class Error and exception handling Time for action – Adding error messages to signup form Disabling the error handler Viewing the error logs Handling 404 redirects Time for action – Adding a custom 404 page Profiling our applications [v]
234 238 241 241 241 243 244 258 258 262
263 263 264 268 269 272 273 273 274 277
Table of Contents
Time for action – Adding profiling to the case study project Summary
Chapter 9: Securing and Deploying Kohana 3 Securing our applications XSS attacks Time for action – Securing against XSS risks SQL injection Cookies and sessions Securing cookies Securing sessions
278 281
283 283 284 285 288 289 289 291
Configuring the application environment Preparing for production Removing unnecessary files Directory structure considerations Multiple Application Configuration
292 294 295 295 297
Updating the Configuration settings Summary
298 299
Appendix: Upgrading from CodeIgniter and Kohana 2 What makes Kohana different from CodeIgniter? Moving from CodeIgniter to Kohana Autoloading classes Strict PHP 5 codebase Included ORM Modules Moving from Kohana 2.x GET and POST methods Directory structure Naming conventions Views Updated ORM Routing and requests Upgrading from Kohana 3.0 to 3.1 Request and response classes Security enhancements Bootstrapping and the front controller Validation library Other changes
Preface The Kohana framework for PHP 5 is a robust library for rapidly creating applications. No previous experience with Kohana, CodeIgniter, or MVC frameworks is assumed or needed to understand and use this book, and any previous experience will only assist you as we learn the Kohana framework from the ground up.
Brief history of Kohana
Before Kohana, there was CodeIgniter. Developed by EllisLab, and the driving force behind Expression Engine, CodeIgniter, gained huge popularity in the PHP community, especially among developers seeking MVC solutions for their growing needs. Eventually, CodeIgniter was forked to bring it up to speed with PHP 5, and Kohana was born. Kohana 2 relied on the open source community for its development, and quickly grew into a production-ready framework, used by developers all over the world. Kohana 3 is a completely rebuilt framework, utilizing a HMVC design pattern, and leveraging the power of PHP 5 like never before. Although Kohana 2 is still being supported, all new applications written with Kohana are recommended to use the Kohana 3 release to allow for a longer support cycle. Kohana 3 is not backwards compatible with Kohana 2 or CodeIgniter, and migration can be difficult. If you have experience with Kohana 2, and would like to see the differences in more details, and understand the process of moving from Kohana 2 to Kohana 3, please take at look at the Appendix: Upgrading from CodeIgniter and Kohana 2. This is also a good resource if you have a Kohana 2 application you want to migrate to the Kohana 3 platform.
Preface
Kohana's user guide and API reference
Kohana ships with a complete user guide and API reference. In Chapter 1, we will install the framework with the user guide module, allowing us to browse the guide from within our application. The official guide is available on the Kohana website at http:// kohanaframework.org/guide/about.kohana
The guide contains basic information about Kohana, installation, configuration, and tutorials. Most importantly, it includes the API reference, which allows easily us to explore and view the methods and properties of every class in the library see. As we build our applications, the guide expands to include our code, documentation, and help files. This can be quite a resource for developing large projects with multiple developers, revisiting old projects for which you need to refresh your memory, and making it generally easier to find your way around. It is a good idea to familiarize yourself with the documentation online, and spend some time looking over the pages and API Reference.
Structure and patterns
In order for Kohana to work properly, some basic patterns and coding conventions must be followed. You are encouraged to follow the Allman/BSD style that is consistent throughout the framework; however, not following exactly will not break your application. That being said, not following certain coding conventions will prevent Kohana from autoloading your files, and thereby breaking your application. Because Kohana embraces Convention over Configuration, the naming of your directories, files, and classes determine how (and if) they are automatically loaded into the framework. This is a great feature as we do not have to write a lot of include() or require() statements, and it eliminates the need for trying to find files before we can use them. Another advantage of using this convention is we can utilize Kohana's cascading filesystem, which allows us to easily extend and override lower-level classes. Essentially, this means we can put a class in a module, and use it directly. All we need to do to extend it in our application is to drop a file with the same name into our application directory structure, and we can begin extending and overloading the class. We will get into this in more detail in Chapter 3. To begin working with Kohana, there are a few very important rules you must know, and we will cover the rest of the nuances as we progress through the book. The main thing to know now is: all class names must adhere to the following conventions:
All classes live in the /classes/ directory, usually located in the application directory or in a module. [2]
Preface
Underscores ( _ ) represent directories, example: Controller_Foo would be located in classes/controller/foo.
All class filenames and directory names must be lowercase.
To get a more detailed look at the conventions and patterns used in Kohana, please visit: http://kohanaframework.org/guide/about.conventions. For more information and examples of Allman/BSD syntax, please visit http://en.wikipedia.org/wiki/ Indent_style#Allman_style_.28bsd_in_Emacs.29
HMVC in a nutshell
Most modern PHP frameworks use, or at least offer, a Model View Controller (MVC) pattern for constructing and organizing your project. The advantages of using Object-Oriented Programming (OOP) were recognized with the release of PHP 5, and the development community quickly began finding ways to create more reusable, lightweight, and expandable code. The MVC pattern fits perfectly for web application development, allowing developers to create code once, and use it across multiple projects. The MVC design pattern allows presentation, data, and logic layers to all be coded separately and used together. Traditionally, MVCs use a front-controller to handle all requests, route and load a controller, and the render the output from a view. Hierarchical MVC (HMVC) takes this approach one step further. Much like AJAX allows web browsers to interact with the server without reloading the page, HMVC allows your serverside application to handle multiple requests without having to display a page every time. Where MVCs allow for one controller to be loaded and executed, the HMVC pattern loads a primary controller, and then allows requests to be created repeating the loop of routing and loading controllers, and allowing for even more requests. Although this can be confusing at first, it is actually quite powerful. Having full control over routing and requests gives developers a lot of flexibility and power when writing code. We will take a much deeper look at the HMVC structure in Kohana when we explore routing and request handling in Chapter 3.
Models, Views, and Controllers
Finding the right place for our code is important, especially as our application grows. Anyone who has ever worked on a project consisting of more than a few pages quickly realizes that code can become convoluted in no time, and good organization pays off. To keep our code clean and DRY (an acronym for Don't Repeat Yourself—a motto meaning write code once), we will use Models, Views, and Controllers to house our data logic, business logic, and presentation files. [3]
Preface
CONTROLLERS – These are the driving force behind our applications. Our controllers are loaded immediately after the request, and will delegate data modeling to our models, and presentation duties to our views. Controllers are where we utilize our models and prepare our data for output.
MODELS – Any external data that needs to be used by our application will live in a model. More often than not, models are used to interact with a database or other data store. We will store any methods necessary for creating, updating, reading, and deleting data from any data stores with which we interact.
VIEWS – Perhaps the easiest way to think of views is just to imagine HTML. These are the files where all of our markup will go, with minimal logic. View files should contain the least amount of PHP code of the three types, and should utilize PHP's alternative syntax for control structures when it is necessary to use server-side scripting.
The above definitions are made with broad strokes, and I do not suggest they completely outline where and how to use each. In Chapter 2, we will take an in-depth look at Controllers and Views, and in Chapter 6 we use Models to interact with our database. For now, knowing the basic use for each of the MVC layers will give you an idea of how it all fits together.
Alternative syntax
It is not uncommon for HTML and PHP to be intermixed within the same document. In fact, many of us began web development by making very long files with PHP and HTML so mixed together, it was sometimes hard to tell them apart. Kohana allows us to keep our business logic in our controllers and models, and our markup in views. Although view files are used for presentation, and mainly consist of HTML code, we will need do have some PHP for dynamic content. Instead of using curly braces everywhere, making our code confusing to read, we will be using PHP's alternative syntax. You can read more about alternative syntax here: http:// php.net/manual/en/control-structures.alternative-syntax.php
Routing and request overview
As explained earlier, Kohana utilizes the HMVC design pattern, and allows us to create requests essentially anywhere within our applications. The request flow is the order in which files are loaded, starting with index.php and moving throughout the framework, loading essential files, processing the requests, and rendering any output. We will examine the request flow and routing systems throughly in Chapter 3, giving examples and analysis of the entire process. [4]
Preface
What this book covers
Chapter 1, Getting Started, covers Kohana 3 installation—displaying the default welcome content and user guide for the framework. Chapter 2, Using Views and Controllers, will jump into using Kohana's controllers and views, and make something more robust than a simple "hello, world" script. Chapter 3, Routing and Request Handling, will talk about the way Kohana routes our requests, and how to use request handling and routing to make our applications more robust. We will also learn more about Kohana's cascading filesystem, the Heirarchy aspect of HMVC, and how Kohana works at runtime. Chapter 4, Working with Helpers and Classes, will look at some of the helper and service-oriented classes provided in Kohana, and take a look at extending their functionality, and creating a few of our own. Chapter 5, Installing and Configuring Modules, will cover how to extend Kohana's core functionality by implementing modules. We will be exploring the modules that ship with the framework, learn how to use third-party modules, and how to create modules of our own. Chapter 6, Interacting with the Database, will take a more detailed look at the Database and ORM modules, their use, and a more in depth look at some of the official modules that use and rely on these very important modules. Working with databases has never been more fun and easy, and in this chapter, we will expand our case study website to make it work with dynamic data, user authentication, and related data. Chapter 7, Using the ORM and Auth Modules, will take an in-depth look at two modules that rely on the Database module to function: the ORM and the Auth modules. By implementing these modules in your projects, you can make working with data even easier and authorizing users a breeze. Chapter 8, Troubleshooting and Error Handling, will be covering troubleshooting and error handling, and we will be able to see how to detect and debug errors in our applications while adding the final touches to the case study site. Chapter 9, Securing and Deploying Kohana, will discuss methods to secure our applications, and keep our code safe from attacks. Appendix, Upgrading from CodeIgniter and Kohana 2, will look at migrating from an earlier version of Kohana and CodeIgniter to the newest version of Kohana.
[5]
Preface
Who this book is for
If you are a developer who understands PHP, has some experience with Object-Oriented Programming (OOP), and has a desire to harness the power of a Hierarchical Model-ViewController (HMVC) framework, then this book is for you.
Conventions
In this book, you will find several headings appearing frequently. To give clear instructions of how to complete a procedure or task, we use:
Time for action – heading 1.
Action 1
2.
Action 2
3.
Action 3
Instructions often need some extra explanation so that they make sense, so they are followed with:
What just happened? This heading explains the working of tasks or instructions that you have just completed. You will also find some other learning aids in the book, including:
Pop quiz – heading These are short multiple-choice questions intended to help you test your own understanding.
Have a go hero – heading These set practical challenges and give you ideas for experimenting with what you have learned. You will also find a number of styles of text that distinguish between different kinds of information. Here are some examples of these styles, and an explanation of their meaning. Code words in text are shown as follows: "Because Kohana utilizes a front controller design pattern, the only application file that must be exposed is our index.php file." [6]
Preface
A block of code is set as follows: foreach (ORM::factory('post', 1)->where('active', '=', 1)->author>find_all() as $author) { echo $author->name . ' '; }
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:
Any command-line input or output is written as follows: $ git clone https://github.com/kohana/kohana.git -b 3.0/master egotist
New terms and important words are shown in bold. Words that you see on the screen, in menus or dialog boxes, for example, appear in the text like this: " By clicking the Edit Message link, you can now edit the message:".
Warnings or important notes appear in a box like this.
Tips and tricks appear like this.
Reader feedback
Feedback from our readers is always welcome. Let us know what you think about this book— what you liked or may have disliked. Reader feedback is important for us to develop titles out of which you really get the most. To send us general feedback, simply send an e-mail to [email protected], and mention the book title via the subject of your message.
[7]
Preface
If there is a book that you need and would like to see us publish, please send us a note in the SUGGEST A TITLE form on www.packtpub.com, or e-mail [email protected]. If there is a in which that you have expertise, and you are interested in either writing or contributing to a book, see our author guide on www.packtpub.com/authors.
Customer support
Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase. Downloading the example code for this book You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.
Errata Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in the text or the code— we would be grateful if you would report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/support, selecting your book, clicking on the errata submission form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted, and the errata will be uploaded on our website, or added to any list of existing errata, under the Errata section of that title. Any existing errata can be viewed by selecting your title from http://www. packtpub.com/support.
Piracy Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works, in any form, on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy. Please contact us at [email protected] with a link to the suspected pirated material. We appreciate your help in protecting our authors, and our ability to bring you valuable content. [8]
Preface
Questions You can contact us at [email protected] if you are having a problem with any aspect of the book, and we will do our best to address it.
[9]
1
Getting Started To start using Kohana 3, you must first learn how to install and configure the framework. Here, you will learn how to install Kohana from the website and via the official Git repository, configure Kohana for development, and display the default welcome page. This chapter includes:
System requirements
Installation from Kohana's website
Installing from Kohana's Git repository
Overview of Kohana's filesystem
Configuring the framework for a development environment
Displaying your welcome page
No previous experience or exposure to Kohana is required to follow the instructions and examples in this book. You will start off by setting up the framework from scratch, examine the necessary steps to configure Kohana, and apply your knowledge by building a real application. By the end of this chapter, you will have a full Kohana 3 installation displaying the default welcome content and user guide for the framework. So, let's get started!
Getting Started
System requirements The exact system requirements for Kohana are somewhat fluid, with newer versions of the framework requiring newer versions of PHP. The basic requirements are essentially the same: a newer version of PHP5, some common extensions, and a few optional extensions that allow Kohana to do more. My experience has been that most hosting providers, and out-of-the-box development servers (WAMP, MAMP, XAMPP, etc), come pretty well suited for Kohana right out of the box. At the time of writing, Kohana requires the following:
PHP 5.2.3 or newer PCRE with UTF-8 Support PHP SPL Enabled PHP Reflection Enabled Filters Extension Iconv Extension Native MBString (not overloaded by extension) Character Type (CTYPE) Extension
The following extensions are not required, however, they are recommended for full functionality. Through the course of this book, you will assume these extensions will be present: cURL, mcrypt, GD, and PDO. Again, most providers and development packages should work fine. Also, during installation, Kohana will check for all the required and optional extensions and let us know if anything is out of order. For more information, visit the Kohana website at http://kohanaframework.org Don't worry about getting too caught up with the system requirements. Everything that is required, even optionally, is open source and free, installed on most systems, and there is a lot of support out there. Let's move right along and prepare to get Kohana installed and running on your system.
Case Study Throughout the course of this book, you will be creating a web application that will allow users to create simple profiles, post messages for their friends, and manage their public page. Because this site is for sample purposes, we will be concentrating on backend code, and not the HTML, CSS, and JavaScript that would normally go into a site scheduled for production. You will create a fully functioning application, and secure it for a production environment. [ 12 ]
Chapter 1
Our sample application will be called "Egotist", and will encourage users to befriend each other and share details about their daily activities. They will be able to post comments, and see comments that are posted by their 'friends'. We will implement basic authorization, session handling, a database for your posts, users, and friendships, and do it all in Kohana.
Downloading and installing from the Web Kohana's website http://kohanaframework.org/ hosts an archive of the latest releases in its download section. Here, you will also find user forums, documentation, and other releases of the framework. The website makes it very easy to obtain and install the most current stable release of Kohana, browse the documentation, and interact with the Kohana community.
Time for action – Downloading from web Let's take a look at installing Kohana from their website:
1.
Open http://kohanaframework.org/ in your web browser.
2.
Navigate to the download page and select the newest 3.0.x version marked Stable.
3.
Clicking the download link will retrieve a file named Kohana-3.X.X.zip (where X could be any number, representing the version of the framework).
4.
Create a web directory called egotist.
5.
Unzip the downloaded file into your egotist site folder on your development environment.
A listing of your /egotist/ directory should contain the framework:
[ 13 ]
Getting Started
What just happened? As you can see, you have unarchived Kohana into your webroot directory. Take note of the mark-down (.md) files, especially those labelled DEVELOPERS, TESTING, and README. It is recommended that you give these files a read. Also, notice the framework comes complete with index.php, a system folder, application folder, and modules folder. Although installing Kohana via the download archive is a quick and easy way of obtaining the framework, the code repository is hosted and maintained on GitHub (http://github. com), and can be accessed and updated via this resource. If you would like to install Kohana via GitHub, the next section is for you.
Installing Kohana from GitHub In a nutshell, Git is a Distributed Version Control System (DVCS) or Source Code Management (SCM) tool written by Linus Torvalds originally designed for the Linux kernel developers. Its popularity, especially amongst open source developers, has grown considerably in recent years, for many reasons. Git provides developers with many features that make working with your code more convenient. Using Git, you can easily create and merge different branches, create forks of existing repositories, and issue pull requests for patches or upgrades made to forked projects. The distributed nature of Git allows every team member to keep the entire repository duplicated in their environment. GitHub is a storage solution for Git repositories, with free hosting for open source projects. Kohana uses GitHub for hosting, and you can get Kohana directly from GitHub. Installing Kohana via GitHub makes it easier to keep your application up to date with the most recent releases of both the Kohana 3 system files, but also any submodules that you may be using that are also hosted on GitHub. When you want to use one of hundreds of actively developed modules for Kohana, installing them using Git will be painless. To access repositories on GitHub, you must first have Git installed on your system. For more information on installing and using Git, please visit http://git-scm.com/. Git is free and open-source, and is available for all popular operating systems. Most providers that offer secure shell access also offer Git.
Time for action – downloading using Git The Kohana repository can be found at http://github.com/kohana/kohana, where you find the most current version of Kohana 3 in the 'trunk' of the repository. Git allows for tagging and branching of projects, and the Kohana team has done a very good job of tagging releases and creating branches for new release cycles.
[ 14 ]
Chapter 1
For your development project, you will simply use the most current stable release in the trunk of the master branch that contains the basic framework with official modules. This is easiest way to keep the current stable version of Kohana for your projects.
1.
First, let's create a place to put your application. For the case study in this book, you will be creating a social networking application called Egotist. So, in your web directory, let's create a directory named egotist.
2.
Now, you can run the following command to retrieve Kohana and place it in your egotist directory:
Git should retrieve the repository, and make a copy of it into your site directory. Remember to execute the command from within your egotist or site root directory.
[ 15 ]
Getting Started
3.
After the clone has completed, you can list the contents of your site root directory. It should contain all the Kohana files with a few files associated with Git and all the submodules in place for your system and modules directories.
Included in the repo are some submodules, the 'official' modules sanctioned by the Kohana 3 development team to be included in the basic installation of the framework. The master Kohana repository keeps these submodule locations up to date. Right now these submodules have not downloaded any files to your system.
1.
Download each of these repositories directly into your project, you will want to update these submodules by running the following: $ gitsubmodule init
[ 16 ]
Chapter 1
2.
Now that you have let Git know about the submodules you want to install, you need to do an initial update to clone all the submodules into your install. $ gitsubmodule update
The update may take a little while. Git will go through each submodule and download its newest stable release.
What just happened? The Git clone available at https://github.com/kohana/kohana.git tells Git to clone, or to make an exact copy of, the repository located at https://github.com/kohana/ kohana.git, the second argument, –b 3.0/master tells GitHub which branch you want to clone, and the final argument tells Git where to place the cloned repository, in this case the egotist directory. In the future, updating the codebase will be as simple as returning to the site root and running: $ git pull $ gitsubmodule update
The first command will pull, or download, the newest content from GitHub in the master branch. The second command updates, or downloads, all the submodules you have installed for your project. Currently, you are only updating submodules that are included with the Kohana install. Later, in Chapter 5, you will be adding modules to your project, and some of these will be installed using Git. Once installed, they can be updated in a similar fashion.
Advanced installation with Git By cloning the kohana.git repository, you got a copy of the entire repository that is meant to build the compressed files for download, and it is not really designed to be cloned into projects for use. The method described above is used by many beginners, but has some drawbacks. First, the repo is now pointed to Kohana/Kohana.git, not the repository you wish to use for your project. Secondly, it has all the commits for the files in the application path, where your files will eventually live for your project. Next, when you update your application, Git will want to merge the changes in your application directory with the repo. Lastly, you are tied to the directory structure that this repo subscribes to, making changes like we will explore later in the book harder, if not impossible.
[ 17 ]
Getting Started
Creating your project's directory structure and using existing repositories on GitHub as submodules will make your application more flexible, and easier to maintain. Although there are a few more steps involved here, the savings in the long run are enormous. If you are running a UNIX-compatible operating system, like Linux or Mac OS X, you can use an install a script written by Kohana's Benevolent Dictator for Life, Woody Gilk, aka shadowhand, which handles this process quite elegantly. The script, and instructions for use, can be found here: https://github.com/shadowhand/kohana-installer. If you prefer to set up your application environment by hand, you can create your directory structure, add the Kohana core repository (https://github.com/kohana/core) to your system directory, add all the modules you need to your modules path, create your application structure, bootstrap, and front controller. This provides you with the most flexibility and is the preferred method for most professional developers. For a full tutorial on installing Kohana in this manner, please visit: http://kohanaframework.org/3.0/ guide/kohana/tutorials/git
Although you can use Git to manage the code for your project, we will leave Git behind here, and move forward with your Kohana project. For more information on Git, please visit http://git-scm.com. Now, your development directory has the Kohana 3 framework with the official submodules in place, ready for configuration. Before you dive into installation, let's make sure your environment is properly set up to handle Kohana 3.
Taking inventory A quick look at the files you have downloaded reveals some files you are familiar with, and others that you may not be. You can see there is the standard index.php file, some mark-down files, a readme file, and 3 sub-directories: application, modules, and system. One of the nice things about Kohana is that it comes with the file structure and core files necessary to get your project up and running very quickly. We will explore Kohana's routing a bit more later on, but it is important to know that index.php will be the first file loaded by the framework. The index file will create the necessary configurations and definitions to run Kohana, and then bootstrap the application by loading application/bootstrap.php. You will be working with bootstrap.php very soon when you begin configuring Kohana for first use.
[ 18 ]
Chapter 1
The application directory is where the majority of your code will live. This is where you will create your application's controllers, models, config files, and views. You will also keep your logs and various other items here, well-organized, and easy to access and update. Unlike previous versions of Kohana and CodeIgniter, all of the classes for your app (Controllers, Helpers, Models, etc) will live in the application/classes directory, in their respective sub-directories (application/classes/model, application/classes/controller, etc). You may remember that helper classes in previous versions of the framework had their own designated place to live, and now simply reside in application/classes, and are not designated has 'helpers'. The system folder houses the core files for Kohana, and although you are free to override any file in the library, you are discouraged from changing the contents of any files in the system directory. If you need to make changes to the Kohana system, you can do so by extending and overriding classes in the framework. We will cover this in more detail when you explore routing and requests in Chapter 3. Any modules you install or create will be stored in the modules folder. Kohana comes with some modules bundled with the distribution, like the user-guide, database, oAuth, and others. There are hundreds of modules available from other developers online, and we will learn how to create your own modules in Chapter 5. Although these modules ship with the framework, they are not enabled by default. We will cover how these modules are activated and used later in the book.
Preparing the Filesystem In order for Kohana to work properly, it must have the ability to write cache and log files to the filesystem. By default, the framework uses application/cache for caching, and application/logs for its log files. You can change these locations in the index.php or application/bootstrap.php files, however, the default location is just fine for your needs. To make these directories writeable, you will need to change their permissions. Using a Linux system, you can accomplish this using the chmod command. On a Windows system, you need to make sure the folder is writeable by your web server. Make sure you are in your site directory, in your case /egotist/.
$ chmod -R 777 application/cache
$ chmod -R 777 application/logs
[ 19 ]
Getting Started
While changing the permissions for your cache and logs on your development environment to full access to all users, on your production servers you will want to enforce a bit of security, and your web server should be the owner or a member of the group with write access to this directory. For development purposes, it is usually okay to leave these directories writable for all users. Next, you might want to create a public folder where you will place your front-facing files. Our application will have CSS and images, and may grow to have publicly accessible media. Let's create a directory named public where you can organize these files as you are building your application.
$ mkdir public
Inside this public folder you can create three directories to house your JavaScript, CSS files, and images:
$ cd public
$ mkdirjs
$ mkdircss
$ mkdir images
Now, you have a place to organize your supporting files for your project, and once you begin development, adding stylesheets, images, and JavaScript files will be a breeze. It is worth noting that when you prepare your app for production, you will move your framework outside of the web root folder for your site, and only expose the files necessary to run your application. Alternate directory structures not only allow you to minimize the available public directories and files, it can also allow you to share the framework and modules across several projects and applications. Many developers set up their development, staging, and production environments all using a shared directory structure. For more information on site structure and filesystem concerns in a production environment, please see Chapter 9.
Testing the environment Kohana ships with a nice install script that will let you know if your web server and PHP installation are properly configured to handle the framework. Most hosting providers that I have used have no problems meeting the system requirements for Kohana 3, and local development environments typically work out of the box. To test the installation on your system we will browse to your local installation, located at http://localhost/egotist (your URL may vary depending on your web server configuration). [ 20 ]
Chapter 1
A successful installation will result in the following page being displayed:
If you have any areas that did not pass, please correct these issues before proceeding with the configuration of the application. For the examples in this book, we will assume that all the optional extensions have passed.
[ 21 ]
Getting Started
Configuring Kohana for Development Environment Kohana needs some setup before you can begin using it. The front controller, bootstrap, and basic config files all need to be set up. You need a place to put your configuration options, and a place to put your public files like stylesheets and JavaScript. We will want to enable modules, set your default timezone, application environment, and prepare your project for use. This may sound a bit daunting, but thankfully, Kohana has done most of the heavy lifting for you. You just need to change a few files. First, let's remove the install.php file in your site root directory. Now that you have tested your system environment, you will not be needing this file any longer. If you plan on re-testing in the future, or testing on other systems, you can simply rename it install_old.php for future use. Now that the install.php is out of our way, Kohana's front controller index.php will continue to load the bootstrap, located in the application directory, and named bootstrap.php.
Time for action – Setting your Timezone We need to tell Kohana a little bit about your system and the desired setup. Let's do this by making a few changes to your bootstrap.php file in the /application directory. Let's begin by opening the application/bootstrap.php file. The first setting in the bootstrap.php file contains your timezone information: date_default_timezone_set('America/Chicago');
You can set your default timezone to any identifier recognized by PHP's Date/Time library. For a complete list of supported timezones, please visit: http://www.php.net/manual/ en/timezones.php. Next, you are able to set your default locale: setlocale(LC_ALL, 'en_US.utf-8');
For more information on setting your locale in PHP, please refer to http://php.net/ setlocale.
[ 22 ]
Chapter 1
What just happened? For most installations, the locale settings will be fine. Both the default timezone and locale can be reset later in your code if you need to do internationalization or have users set their local timezones for example. Although having the right time is nice, it is much more important to have working URLs. Without them, your users will never get to view or use your application. Next, you will let Kohana know about your URL scheme. After these settings, you will see some setting for autoloading, SPL, and application environment. For your development setup, you will leave these at their default values. We will delve into the production ready setup in Chapter 9.
Configuring URL settings Below the environment setting block, you see the Kohana::init() method call, initializing Kohana and allowing us to pass an array of default options to the framework in the form of name/value pairs. For now, you are only concerned with your base_url and index_file settings. We will use the URL http://localhost/egotist/ for your base URL. You will want to use the URL that points to your Kohana installation. For local installations, it is common to use the http://localhost/ domain; if you are developing on a live server, you might want to use that URL. Either way, it is recommended to use the full path to your site here, as it will be used later for accessing files, creating links, and all sorts of goodness.
Time for action – setting base URL 1.
In your bootstrap.php file, find the following block of code: Kohana::init(array( 'base_url' => '/› ));
2.
Now update the array being passed to Kohana::init() with the following values, using your base URL for the base_url value: Kohana::init(array( 'base_url' => '/egotist/›, ‹index_file› => ‹› ));
[ 23 ]
Getting Started
What just happened? Here you set the base URL for your application, and have the opportunity to define your index file that will act as your front controller. For the case study site, we will hide the index file, so this can be left blank. Hiding the index file is an optional step, however, all the example URLs for your local site will reflect this change, and will not include the index.php file in your URLs. This is a very common practice that creates clean, readable URLs for your applications. You will need to configure your web server to reroute all requests to your index.php file, so it will not be necessary for users to include it in the URL. If you do not set it to blank here, Kohana will include it when it creates URLs for us when you use the built in helper methods.
Setting up the Application Environment When developing your application, you may need some features to be accessible only in your development environment, and others only when in production. This may include profiling, additional information, features not ready for prime time, error stacks, and other code that is only meant for certain environments. You could even have configuration options based on environment settings.
Time for action – Setting Kohana::$environment 1.
For the time being, let's define your KOHANA_ENV constant right after the "Configuration and Initialization" comment in your bootstrap.php file by adding this line: Kohana::$environment = Kohana::DEVELOPMENT;
2.
The next block of code in the bootstrap.php reads as follows: if (isset($_SERVER['KOHANA_ENV'])) { Kohana::$environment = $_SERVER[‹KOHANA_ENV›]; }
What just happened? With your constant defined, the static property Kohana::$environment will be set to 'development', allowing your application to perform tests against the static property and determine your application environment. Although this is the default setting for the environment, now you have a place to easily change between the environment settings for testing and learning. The four constants that ship with the framework for defining the environment are PRODUCTION, STAGING, TESTING, and DEVELOPMENT. [ 24 ]
Chapter 1
For your case study, you will be using two environment settings: Kohana::DEVELOPMENT and Kohana::PRODUCTION. When you prepare your application for deployment on your production server, you will want to change the value of KOHANA_ENV to production.
Enabling modules Lastly you will want to look at the Kohana::modules method, and the array of options that are commented out. As you build your application, you will be utilizing several of these modules, adding other modules, and creating your own.
Time for action – Enabling the User Guide Let's remove the comments for the 'userguide' module so your code block looks like this: Kohana::modules(array( // 'auth' => MODPATH.'auth', authentication // 'cache' =>MODPATH.'cache', backends // 'codebench' => MODPATH.'codebench', // 'database' =>MODPATH.'database', // 'image' =>MODPATH.'image', // 'orm' => MODPATH.'orm', Relationship Mapping // 'oauth' => MODPATH.'oauth', authentication // 'pagination' =>MODPATH.'pagination', // 'unittest' => MODPATH.'unittest', 'userguide' => MODPATH.'userguide', documentation ));
// Paging of results // Unit testing // User guide and API
What just happened? The Kohana::modules() method accepts an array of key/value pairs representing a module and its path. The default modules that ship with Kohana are listed for us, and commented out to prevent them from loading without being activated and configured. When you create or add modules to your project, you will want to add them here. To use any of the modules that ship with Kohana you simply uncomment the line. You should know that most modules require some configuration. We will be getting into modules for Kohana more in Chapter 5. Save your bootstrap.php file and see what it takes to get a basic configuration file in place so you can keep your application's options in one, easy to find place. [ 25 ]
Getting Started
Creating a default config file As you create parts of your application, you are going to want to access config files. When you set up your database, use sessions or cookies, install or create modules, or have information that may change, you will want to organize these options in one place. In your application directory, there is a sub-directory placed there for us called config. In this folder, you can create configuration files, and access them via a static method. For now, you will load some basic data into a config file for Egotist. In application/config, create a file called site.php and insert the following code: 'Egotist', 'tag_line' => "Let's talk about me!" );
Now, whenever you want to access your site name and tag line, you can do so using the following code: $site_config = Kohana::config('site');
This will find your file named site.php in the application/config folder, and load the array into $site_config. You could then access your config options like so: $site_name = $site_config['name']; $tag_line = $site_config['tag_line'];
We do not need to load the entire array if you only need a single config option. If, for example, you needed the app_name variable only, you could get it using this: $site_name = Kohana::config('site.name');
Kohana will return the value of the array for any keys using the above dot syntax. This allows us to retrieve the value without having to get the entire array. Kohana also allows us to nest arrays within your config files. You can change your application/config/site.php file to read as follows: return array( 'name' => 'Egotist', 'details' => array( 'tag_line' => "Let's talk about me!", 'alt_tag_line' => "Today's subject: ME!"; ); );
[ 26 ]
Chapter 1
We can still access the array directly: // Load site config array $site_config = Kohana::config('site'); // Echo site name and details echo $site_config['name']; // Egotist echo $site_config['details']['tag_line'] // Lets talk about me! echo $site_config['details']['alt_tag_line'] // Today's subject: ME!
Alternatively, you can access nested options using the dot syntax method described above: echo Kohana::config('site.name'); // Egotist echo Kohana::config('site.details.tag_line'); // Lets talk about me! echo Kohana::config('site.details.alt_tag_line') // Today's subject: ME!
As you build your application, your config folder will contain files for your database, sessions, modules, and other files and components. This will allow us to keep your code DRY (an acronym for Don't Repeat Yourself) and easy to maintain. Let's save your site.php config file and take a look at configuring your web server to properly handle URL rewriting for your project.
Server configuration For Kohana to function properly, it needs to rewrite all incoming URLs to route through your index.php file. Kohana ships with a file named example.htaccess, which provides a very good outline for what is needed to accomplish this task on an Apache web server.
Configuring Apache Let's begin by opening and examining example.htaccess: # Turn on URL rewriting RewriteEngine On # Installation directory RewriteBase /site_name/ # Protect hidden files from being viewed Order Deny,Allow Deny From All # Protect application and system files from being viewed RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L] # Allow any files or directories that exist to be displayed directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # Rewrite all other URLs to index.php/URL RewriteRule .* index.php/$0 [L] [ 27 ]
Getting Started
You will want to alter the RewriteBase to contain the location that you put in bootstrap. php for base_url. For your example site, it would read: RewriteBase /egotist/
This lets Apache know where to find your site, and to rewrite all requests to /egotist/ index.php/and allow Kohana to do its magic. You will need to save the file as .htaccess, or save and rename it without the example prepended. Most web hosting providers allow .htaccess files in their web root folder to make alterations to Apache's default configuration.
Configuring nginx Of course, you are not required to use Apache as your web server. There are other web servers that are gaining popularity, one of the most notable being nginx. The official Kohana documentation shows this configuration for nginx: location /egotist/ { index index.php index.html index.htm; try_files $uriindex.php; } location = index.php { include fastcgi.conf; fastcgi_pass 127.0.0.1:9000; fastcgi_indexindex.php; }
For more information on configuring Kohana for clean URLs with Apache or nginx, please visit: http://kohanaframework.org/3.0/guide/kohana/tutorials/clean-urls.
Testing your configuration If everything goes well, you should see the default Kohana welcome controller's "hello, world" greeting, letting us know all is right with your application so far. Let's browse to your site and check it out:
[ 28 ]
Chapter 1
Kohana has a very simple default welcome page, showing us the ever popular "hello, world!" text. Later, we will be replacing this with a more robust page, but for now, this is much better than an error page. Although this lets us know you are viewing your basic welcome controller properly, you will also want to make sure the URL rewriting is working properly, and your userguide module has loaded. To do this, you can browse to http://localhost/egotist/guide, where you can view and browse the official Kohana user guide:
[ 29 ]
Getting Started
Looks like you are all set! This is a local copy of the Official Kohana User Guide, and it is included with every installation. You know your setup is working correctly by viewing this page for three reasons:
Our modules are configured correctly, or you cannot see the User Guide
Our .htaccess are set up right, or you would need index.php in the URL
bootstrap.php is also properly configured
If you are unable to view the User Guide from your local installation, be sure to check the settings in your .htaccess and bootstrap.php files. The API Browser will show all the classes used by Kohana, including classes you create or modify. By exploring different classes, you will find the ability to drill down into methods and properties, making it easy to learn more about Kohana's functionality. While you build your application, you will be paying close attention to proper documentation techniques, and will be expanding the API Browser as you code, simplifying the documentation process, and making your lives much easier when you need to go back and see how something works. In Chapter 8, we will delve further into using and expanding the User Guide. For now, it is important to know it is here, and take the time to explore and familiarize yourself with its content and layout.
Pop quiz – Installation and Configuration Throughout the course of the book, you will be testing your knowledge while reviewing what you have recently learned. You are encouraged to test yourself when these sections appear to ensure you have a good understanding of the topics you are covering. 1. For the framework to run properly, it must have write access for logging and caching. Which directories must be modified to allow for these actions?
application/system and application/logs/
application/cache/ and application/logs/
kohana/cache/ and kohana/logs/
egotist/logs/ and application/kohana/
[ 30 ]
Chapter 1
2. When you configure Kohana for your server environment, you must set the base_ url and index_file. Which method is used to set these options?
Kohana::$environment
Kohana::init()
Kohana::config()
Kohana::base_url()
3. Config options can be accessed as either a complete array of all options for a particular configuration file, or directly using dot notation. Which of the following examples would return the background color from a config file named test.php?
Kohana::config('background-color', 'test')
$backgroundColor = Kohana::config('test')
Kohana::config('test.background-color')
Kohana::config['test']['background-color']
Summary So far you have accomplished a lot. You reviewed the system requirements necessary for your environment. Then you learned how to install Kohana from both the official website and GitHub repositories. Next, you prepared the filesystem and tested your installation using the Kohana Environment Test run via install.php. After passing your install test, you configured your application for development—preparing yourselves to build on a stable foundation. Our Egotist application has its environment variable set, you have created places for your config files, stylesheets, JavaScript, and images. Lastly, you were greeted with the sign of a working Kohana installation, "hello, world". We also discussed using the install test script, preparing for production, and how to keep your Kohana installation up to date using Git. Now that we've learned how to install and configure Kohana for development, let's go develop something! In the next chapter, you will jump in to using Kohana's controllers and views, and make something more robust than a simple "hello, world" script.
[ 31 ]
2
Using Views and Controllers One of the best features of the MVC pattern is the clear separation of logic and presentation. Kohana's structure makes it even easier by providing the basic file structure and files needed to quickly get our app going. In the last chapter, we configured Kohana and displayed the default controller. In this chapter, we will:
Learn the anatomy of a view file
Learn the anatomy of a controller
Create and extend controllers
Create and use controller actions
Create view files
Implement templates
Create an application controller
By the end of this chapter, we will have created controllers and views, utilized Kohana's template system, leveraged some of the power of the HMVC pattern, and used it all together to bring our first bit of interactivity to the case study site. Let's get started!
Using Views and Controllers
What are controllers and views? Simply put, controllers handle the request and pass data between models and views, while views display the output for the user. Controllers are classes that contain specialmethods named action methods or controller actions that can be called via a request. The action methods can then send data to models, compute data, render views, call requests, or all of the above. Most of the controllers we will create will be defining actions that render views as web pages. Controllers in Kohana are organized in the classes sub-directory of either an application, directory, or a module. For this chapter, we will be working in the application directory of our site root, and we will work with modules later in Chapter 5. In a typical web-based application, views will contain primarily HTML, either hard-coded or as generated output. The controller can pass data to the views, and can render multiple views on to one page. Views can therefore be nested, used as templates, or called as needed for different actions.
Anatomy of a view A view can be any markup that we plan on presenting to the user in our applications. As web developers, we use views to create the final HTML that is displayed to our users. As we create applications, we will be creating many view files: some for our headers, footers, navigation menus, and so on. When we put them all together, then pass some data to them to make them data-driven, the final product will be a fully rendered web page. View files will contain a mixture of HTML and PHP using alternative syntax. It is considered a best practice to only use scripting in the views to render output, and not to perform business logic: that is what models do. By following the practice of building skinny controllers and fat models, our views will only have to display the rendered material. In Kohana, by default, view files are stored in the application/views/ directory when used in the application, and in modules/<module name>/views/ when used in a module. For now, let's just focus on the application/views/ directory. In this directory, we can organize our view files in sub-directories to our heart's content. For our case study application, we will be creating directories to hold different view files, and then calling them from our controllers and making them dynamic. We can also render views from within other view files. This would be used in a header view calling a nested navigation view, for example.
[ 34 ]
Chapter 2
Anatomy of a controller Let's open the default controller application/classes/controller/welcome.php in our site root directory. It should look like this: request->response = 'hello, world!'; } } // End Welcome
Reading through this code, we can learn a good deal about our controller. Let's begin with the first line:
This checks to see if SYSPATH is defined, thereby checking if the controller was loaded after index.php, ensuring the framework is loaded and the script was not called directly. Any attempt to access this controller directly will result in the script failing and displaying the text inside the die()construct. classController_Welcome extends Controller{
Here the class is declared, and we know it is extending the Controller class, thereby inheriting all methods and properties from the Controller class. There are a few rules to which we must adhere when creating and naming controllers:
The file must be located in a classes/controller/subdirectory, usually in the application directory or in a module.
The filename of the controller must be all lowercase and end with .php.
The class name must match the capitalized file name with Controller_ prepended. All underscores are converted to directory seperators, so a class named Controller_Welcome would be located at classes/controller/welcome. php. Directory namespaces can be prepended also, so a class named Controller_ User_Profile would be located at classes/controller/user/profile.php.
The class must extend Kohana's base Controller class.
We see that the class meet the above four requirements. The file the controller lives in is named welcome.php, and is located in application/classes/controller/, meeting the first and second rules as outlined above. The class is named Controller_Welcome, where welcome is the filename capitalized, and Controller_ is prepended, passing the third rule. The Controller_Welcome class extends Controller, passing the last rule. [ 35 ]
Using Views and Controllers
The next line of code reads: public function action_index()
This creates a new controller action named index. Controllers, like any class, can contain private, protected, and public methods and properties, and can inherit and override properties and methods from their parent classes. Any method in a Controller class in Kohana that is public, and prefixed with action_ is considered a Controller Action and can be directly accessed by our users, and it is the method that will perform tasks for our applications. In Chapter 3, we will take a more detailed look at routing and request handling, but for now we need a to understand how Kohana will route user requests by default. A URL that looks like this: http://yoursite.com/welcome/index
will be sent to index.php by our server rewrite rule, and will be routed to the Welcome_Controller class and the index action. When we do not specify a controller, Kohana will default to the action_index() method. The following URL would be the same: http://yoursite.com/welcome
In our application/bootstrap.php file, we have the following code block: Route::set('default', '((/(/)))') ->defaults(array( 'controller' => 'welcome', 'action' => 'index', ));
Currently, our application will automatically call the index action of the welcome controller if no other route is defined. This is why the output of the Welcome_Controlleraction_ index() method is called when we do not specify anything in our URI. Finally, inside our action_index() method, we see: $this->request->response = 'hello, world!';
This line sets the response to a string of 'hello, world!', and it is then displayed on the screen for all to see. Because we will be using views for presentation, we will not be printing or echoing text directly from our controllers; that is what views do, after all.
[ 36 ]
Chapter 2
Updating our Welcome Controller Currently, our site only shows the words "hello, world". To remedy this, we will have our index action render a view, and pass some data to the view to be displayed. This is a process that we will repeat a lot when creating sites with Kohana, and we will be expanding on the Controller/View relationship with almost every addition to the application. You are encouraged to follow along with the examples in this chapter, and to consult the source code that accompanies this chapter online.
Time for action – Getting our controller action to use a view file A vanilla Kohana 3 installation contains a default route to a controller named welcome. php. To get our feet wet with controllers, let's open and manipulate the default welcome controller.
1.
Locate and open application/classes/controller/welcome.php.
2.
Modify your action_index() method as follows: public function action_index() { $this->request->response = View::factory('welcome'); }
3.
Create and open a file named welcome.php in application/views/.
4.
In the application/views/welcome.php file, insert the following code:
Welcome to Kohana 3
This is thewelcome.php view
[ 37 ]
Using Views and Controllers
5.
Enter the URL http://localhost/egotist/ into a browser and view the output:
What just happened? By altering the index action in our Welcome Controller, we were able to call a view file and render it as our output. Let's take a look at this line in the action_index() method: $this->request->response = View::factory('welcome');
The View class has a static method factory() that accepts two parameters: one for the view file, and a second for an array of values to be used by the view. The factory() method returns a new View object. Kohana will auto-render the view file passed unless it is told to do otherwise. The method completes and renders our welcome.php view file, displaying our HTML in the browser as shown above.
Passing data to the view As mentioned earlier, one of the main features of controllers is their ability to pass data to the view files. This is what allows us to keep our logic in the controllers and models, and our presentation in the view. There are several ways to accomplish this, and we will explore a few below.
[ 38 ]
Chapter 2
Using the factory( ) method In our current action_index() method, we are creating the View object using the factory() static method. The method accepts a second argument of an array that is then accessible in the view file. A few small changes will turn our static welcome page into a dynamic view.
Time for action – Passing data via the factory method To create and output data from our controller, we will need to modify both the controller action and the view file.
1. 2.
Open the controller file located at application/classes/controller/ welcome.php.
Modify our action_index() method as follows: public function action_index() { $data = array(); $data[‹site_name›] = ‹Egotist›; $data[‹random›] = rand(1,10); $this->request->response = View::factory(‹welcome›, $data); }
3.
Now, we will need to open our view file located at application/views/ welcome.php and modify the file as follows:
Welcome to
is a number between 1 and 10
[ 39 ]
Using Views and Controllers
4.
Refreshing the browser to view the output, we see the following:
What just happened? In the controller's index action, the $data array was passed as the second argument in the View object's factory method. This array contains two values: one is a string that contains the site name, and the other is a random integer between 1 and 10. These array key/value pairs are accessible in the view script as local variables.
Using view variables Currently, we are creating the data for the view file before instantiating the View object, and then passing the array of view variables into the view's factory method. Creating and managing an array of data for the view can be cumbersome and confusing. Thankfully, this is not our only option. We know an instance of the View object is being returned by our factory method, which we are then passing to the Request object to render our view. We can manipulate the instance of the View and use its magic __set() method to create view variables.
Time for action – Passing data as view variables To create and output data from our controller, we will need to modify the controller action, removing the $data array and replacing it with a $view variable:
1.
Open the controller file located at application/classes/controller/ welcome.php. [ 40 ]
Chapter 2
2.
Modify the action_index() method as follows: public function action_index() { $view = View::factory(‹welcome›); $view->site_name = ‹Egotist›; $view->random = rand(1,10); $this->request->response = $view; }
3.
Refresh your browser and view the output, the page should be exactly the same (with a new random number).
What just happened? Instead of creating the array of key/value pairs we want to display in the view, we are creating an instance of the View object by calling the static method factory(). Using the factory method, we are then sending the object directly to the Request object. In this example,we added values in the object that will be available as variables in the view script. There were no changes necessary to the view file, because the same names were used for our view variables. If we had added or changed the names of the variables, we would have had to make the same changes to the view script.
Using the set( ) method The View object has another method that can be used to pass variables to the view. By using the set() method, we can add data to the view by setting the name as the first parameter, and the value as the second. Because factory() returns an instance of the View object, we can chain the set() method to it. Additionally, set() also accepts a key/value pair as a single argument.
Time for action – Passing data using the set() method To create and output data from our controller, we will need to modify the controller action; however, the view file will remain the same.
1.
Open the controller file located at application/classes/controller/ welcome.php
2.
Modify the action_index() method as follows: public function action_index() { $view = View::factory(‹welcome›) [ 41 ]
Refresh your browser and view the output; it should be exactly the same.
What just happened? The View object's factory() method returns an instance of the View object, and then we chained the set() method with parameters for our view variable names and values. This could also be written with the same outcome as: public function action_index() { $view = View::factory('welcome'); $view->set('site_name', 'Egotist'); $view->set('random', rand(1,10)); $this->request->response = $view; }
Using the set() method makes it easy to read code, and can be a good pattern to use when the values for the view variables will be computed elsewhere, and then set once the view is instantiated. Again, there were no changes necessary to the view file because the same names were used for our view variables.
Using the bind( ) method Creating view variables, and then passing their values by reference can be a powerful way to ensure the variables we need in our view are assigned to the view, even if we have not created the values for the variables yet. Kohana's View class gives us the bind() method to do just that.
Time for action – Passing data using the bind( ) method To use the bind() method, we will once again be modifying the index action in the Welcome Controller.
1.
Open the controller file located at application/classes/controller/ welcome.php
[ 42 ]
Chapter 2
2.
Modify the action_index() method as follows: public function action_index() { $view = View::factory(‹welcome›) ->bind(‹site_name›, $site_name) ->bind(‹random›, $random); $site_name = ‹Egotist›; $random = rand(1, 10); $this->request->response = $view; }
3.
Refresh your browser and view the output; it should be exactly the same.
What just happened? Just like the previous example, where we used the set() method to pass variables to the view chained to the factory() method, this example adds variables to the View object. The main difference here is the variables being set do not exist yet, and are being passed by reference behind the scenes. This allows us to create all the view variables necessary for the action to execute properly at the same time we create our view object. If we forget to set the $site_name variable, like so: public function action_index() { $view = View::factory('welcome') ->bind('site_name', $site_name) ->bind('random', $random); $random = rand(1, 10); $this->request->response = $view; }
There would not be an error in the view that $site_name was not set. It will simply be null, and not display any text for that spot in the view. This may be desirable in places where we do not need to worry about whether the variable is null, however, it may be better to use the set() method, and then use PHP's isset() to determine whether the variable is available.
[ 43 ]
Using Views and Controllers
Putting it all together In the last few pages, we have learned several ways to pass data to, and display data in, our view scripts. Let's see how this can apply to our welcome page.
Time for action – Passing data using multiple methods Once again we will be altering the Welcome Controller and View scripts. This time, the objective is to leverage our new knowledge of how data works in our controllers, and how to properly set and display the data in the view.
1. 2.
Open the controller file located at application/classes/controller/ welcome.php.
Modifythe action_index() method as follows: public function action_index() { $view = View::factory(‹welcome›) ->bind(‹random›, $random); $random = rand(1, 10); $view->site_name = ‹Egotist Beta›; $this->request->response = $view; }
3. 4.
Now, we will need to open the view file located at application/views/ welcome.php.
Modify the view file as follows:
Welcome to
is a number between 1 and 10
5.
Refresh your browser, and view the output:
[ 44 ]
Chapter 2
What just happened? The random number is set using the bind() method, allowing us to perform the logic associated with the number later in the method. We then used the magic set method to create a view variable for $site_name, and gave it a new name of "Egotist Beta" so we could see the difference when we refreshed our browser. In the view script, we are now checking for the existence of $site_name prior to displaying it, and defaulting to "Egotist" if the $site_name is not specified. Removing the following line of code in our Welcome Controller's action_index() method will prevent $site_name from being set as a view variable: $view->site_name = 'Egotist Beta';
A quick refresh would show that the app did not break, because we were checking to make sure the variable was set before we printed it out.
Pop quiz – Controller conventions and View variables 1. In order for a controller to be valid, it must be located in the classes/ controller/ directory of the application or module, the filename must be lowercase, and the class should map to the filename and have Controller_ prepended to it. Also, every controller must extend which class? a. class Kohana_URL b. class Kohana_HTML c. class Controller d. class HTML [ 45 ]
Using Views and Controllers
2. There are several ways to pass view variables from controllers to views. Which method allows you to pass a view variable by reference, so you can manipulate it without having to repass? a. set() b. bind() c. _refer() d. __set()
Using the Template Controller Kohana ships with a Template Controller that makes it easy to implement layout and template files for rendering our site's pages. Essentially, the Template Controller sets an object property for the template and instantiates a View object. The template controller also sets auto rendering to true by default, and sends the template to the Request object. Using Kohana's Template Controller makes it faster and easier for us to create our web applications, and allows for less work in our controller actions. In our case study site, we will be implementing the Kohana Template Controller, and creating base controllers that we will extend as the application grows.
Time for action – Extending Kohana's Template Controller Once again, we will be altering the Welcome Controller and View scripts. This time, the objective is to leverage our new knowledge of how data works in our controllers, and how to set and display the data in the view properly.
1. 2.
Open the controller file located at application/classes/controller/ welcome.php.
Modify the class declaration so it reads: ClassController_Welcomeextends Controller_Template {
3.
Modify the action_index() method as follows: public function action_index() { $content = View::factory(‹welcome›) ->bind(‹random›, $random); $random = rand(1, 10); $content->site_name = ‹Egotist Beta›; $this->template->content = $content; } [ 46 ]
Chapter 2
4.
Now, we will need to create a template view file in application/views/ named template.php.
5.
Add the following code to the new application/views/template.php file: Egotist App
6.
Refresh your browser and view the output; it should appear to be the same.
7.
In your browser, view the page source to inspect the HTML that was generated. It should look like this:
[ 47 ]
Using Views and Controllers
What just happened? We started out by changing the inheritance of our Welcome Controller class. As we learned earlier, all of our controller classes are required to extend Kohana's Controller class. The Controller_Template class provided by the framework does indeed extend the base Controller class, so our Welcome Controller is the grandchild of the Controller class, so all is good there. By extending the Template Controller, we gain the ability to use a view file as our template (or layout) file, and then pass data to that template. In the action_index() method, we changed our welcome view from being set in a variable named $view, and instead had it assigned to a variable named $content. Then, we removed the call to $this->request->response, and instead had the object go to $this->template->content. The reason for this is simple: the Template Controller creates a View object for us, and we are simply assigning view variables to it. We will cover the Template controller in more detail in the following section. Next, we created the application/views/template.php view file. It contains the basic doctype declarations and structure for our HTML document. Inside the body tag, we are echoing the $content variable, and this is where our welcome view is being displayed. As we can see, using Kohana's template controller is very useful for keeping our view files clean and organized. As our application grows, having templates, organized views, and thin controllers will make the application stronger, and our stress levels lower. To best cover the Template controller, now would be a good time for us to open up the source code for the controller and get a better understanding of how it works.
Examining the Template controller Looking over the source code for Kohana classes is one of the best ways to learn how to properly use the framework. Opening the Template controller located in system/classes/ kohana/controller/template.php reveals the following code:
Chapter 2 */ abstract class Kohana_Controller_Template extends Controller { /** * @var View page template */ public $template = 'template'; /** * @varboolean auto render template **/ public $auto_render = TRUE; /** * Loads the template [View] object. */ public function before() { if ($this->auto_render === TRUE) { // Load the template $this->template = View::factory($this->template); } return parent::before(); } /** * Assigns the template [View] as the request response. */ public function after() { if ($this->auto_render === TRUE) { $this->request->response = $this->template; } return parent::after(); } } // End Controller_Template
Here, we see this class is actually very simple, and it acts as an intermediary between the base controller for the framework and our concrete implementations in our applications directory. This class has two public properties, auto_render and template, which can be overridden to modify the functionality easily. Next, the before() function is just creating an instance of a View object for the template, while the after() method is creating the response body. [ 49 ]
Using Views and Controllers
The Template controller is very light, and simple, but provides an efficient and elegant way to organize our layouts while providing a base for us on which to build. This theme is very common through out the framework.
Have a go hero – Explore controllers and view variables Now that we have implemented a template and view variables, there is a lot of fun to be had. You can experiment by passing different variables in different ways. You can even mix and match the different methods of passing view variables all in the same action method. Try having the controller generate the date, compute sunrise, and return the data to your view. As you manipulate the template, view, and view variables, you will get a better understanding of what is happening and how it all fits together. Now would also be a good time to explore the code in the system/ directory. Find the Controller classes and see what Kohana is doing. This will let you see first hand the code you are extending, and give you a better understanding of what is under the hood.
Setting global view variables As we learned in the previous example, the template is simply a view that has view variables. In the case above, the $content view variable in the template was another view, in this case the welcome view. In the index action we assigned some view variables to the content view that are only accessible in the welcome.php view file, as that is the object on which they are set. With our template, we may want to add the ability to set the page title from the controller, and have it displayed in the section of our template file. If we also want to access this view variable in the welcome view, we will need to set it as a property on that object also. To avoid having to do this, Kohana allows us to set global view variables that we can access from any view we are rendering.
Time for action – Setting global view variables Just like the set() method we used earlier to set view variables for a specific View object, in a similar fashion, we can set global view variables for use in multiple views.
1. 2.
Open the controller file located at application/classes/controller/ welcome.php.
Modifythe action_index() method as follows: public function action_index() { View::set_global(‹site_name›, ‹Egotist Beta›); [ 50 ]
Open thetemplate.php file located in application/views/.
4.
Modifiy the template file to show the $site_name variable for the title tag:
[ 51 ]
Using Views and Controllers
5.
Refresh your browser and view the output; it should be exactly the same, however, the page title should now be Egotist Beta. A quick glance at the source code reveals:
What just happened? By using the set_global() static method available in Kohana's View class, we created a view variable named $site_name that can be accessed directly from any view file rendered. We then successfully named the site name variable from both the template view and the welcome view that was rendered inside the body tags in the template. Viewing the page source lets us see that the title tag was properly printed, and the content variable successfully output the welcome view, complete with $site_name and the bound random number.
Binding global view variables When we began setting view variables earlier in this chapter, we also learned how to bind variables to be passed by reference to our views. By binding the variables, we guaranteed they were set as assigned as variables before we had done anything with them. We can also bind global view variables in a similar fashion.
[ 52 ]
Chapter 2
Time for action – Binding global view variables Just like the bind() method we used earlier to bind view variables for a specific View object, we can bind global view variables for use in multiple views.
1. 2.
Open the controller file located at application/classes/controller/ welcome.php.
Modify the action_index() method as follows: public function action_index() { View::bind_global('site_name', $site_name); $site_name = 'Egotist Beta'; $content = View::factory('welcome') ->bind('random', $random); $random = rand(1, 10); $this->template->content = $content; }
3.
Refresh your browser and view the output; it should be exactly the same markup.
What just happened? The bind_global() static method in Kohana's View class allows us to bind a view variable in the same way set_global() lets us set one. By binding the $site_name variable as a global view variable, we were able to pass the $site_name variable by reference, and then set it later in the controller without having to re-assign it to the view. Because we were just changing the way we passed the variable, and not the name of the variable itself, there were no changes necessary in our view files.
Beyond the Welcome Controller Our Welcome Controller is now extending the Template Controller, which in turn is extending Kohana's Controller class. As our application grows, each controller that extends the Template Controller will most likely have redundant information that needs to be set. This could be navigation items, document titles, generated keywords, or any other information that may be set in every controller to be used in the template. Before we can create a new controller to sit between the Template Controller and the controllers with which we will build our application, we need to know how to create a new controller from scratch. [ 53 ]
Using Views and Controllers
Creating new controllers Up to this point, we have been manipulating the welcome controller that was included in our Kohana 3 installation. Although we have covered the requirements for creating controllers in Kohana, nothing beats actually jumping in and making a new controller. For the case study application, Egotist, the user will need to see some basic pages like about us, why Egotist, and so on. Although in many cases, these may be content-managed pages that are dynamically called from a database, we have not yet covered data stores in Kohana, and will be rendering these pages from view files. To do this, we need to create a controller for viewing pages, and then create the view files necessary.
Time for action – Creating our first controller We already know the rules to create a new controller, and have experience manipulating the action methods to generate the outcome we desire. With this knowledge in place, it is time to put it to action and create a controller for viewing pages in our application.
1.
Create a new file located in application/classes/controller/ named page. php.
2.
Open the page.php file for editing, and insert the following code: template->content = $content; } }
3. 4.
Because we are asking for a view file located at pages/about, we will need a view file for the action to call. To accomplish this, create a new directory in application/views/, and name it pages. In the new directory, application/views/pages/, create a file named about. php.
[ 54 ]
Chapter 2
5.
Open application/views/pages/about.php, and insert the following code:
About Egotist
We could go on and on about ourselves, and we think you could too! That is why we build this wonderful application, so people like you could tell the world every little thing you were doing.
6.
Direct your browser to http://localhost/egotist/page/about; it should look similar to this:
What just happened? By following the naming conventions for our controller filename, class name, and action methods, we were able to create a new controller that uses our template file and displays a HTML web page in just a dozen or so lines of code. Because we are using Kohana's template controller, we only had to set the content view variable with our desired view, and also set the $site_name so the view template would properly print the title tag for our about page. Let's add another page to our page controller and see how this looks with more than one action method.
Time for action – Expanding on our first controller Now that we have a pages controller, it will be easy to add static pages to our site. Let's start by adding another page that explains the benefits of using Egotist.
1.
Locate and open the file located at application/classes/controller/page. php. [ 55 ]
Using Views and Controllers
2.
Open the file for editing, and insert the following code, under our action_ about() method: public function action_why_egotist() { View::set_global(‹site_name›, ‹Egotist Beta›); $content = View::factory(‹pages/why_egotist›); $this->template->content = $content; }
3.
In the application/views/pages/ directory, create a file named why_egotist.php.
4.
Open application/views/pages/why_egotist.php, and insert the following code:
Why Egotist?
Because the world wants to know when you:
Go to the store
See a movie
Want to share a personal thought or feeling
Need to vent about nothing
If constantly trying to let all your friends know just how cool you are, Egotist is the app for you!
5.
Direct your browser to http://localhost/egotist/page/why_egotist; it should look similar to this:
[ 56 ]
Chapter 2
What just happened? Okay, now this is getting too easy. By simply creating another controller action and following the naming conventions, we were able to add another page by just creating the view content, setting the global view variable, and calling the view file for the page in question. There are a few issues here that should be addressed, though. Let's take a look at our new controller in its entirity. Opening and viewing the file application/classes/ controller/page.php reveals the following code: template->content = $content; } public function action_why_egotist() { View::set_global('site_name', 'Egotist Beta'); $content = View::factory('pages/why_egotist'); $this->template->content = $content; } }
In both the action_about() and action_why_egotist() methods, we are setting the same global view variable, and this is creating unneeded repetition. Also, the application is extremely simplistic now, but as it grows, there may be many variables being set and passed to the template and different views; we may even have multiple template files at some point (think mobile vs. desktop). Each time we create a new feature on which all pages depend, we will need to add them to each and every controller action. This could get ugly. To remedy this, we can put all the redundant code in a constructor, or extend the before() statement as we will see later, or create a service method. Each of these options would work, but then we would need to duplicate this effort in every new controller. The best option is to create a base controller, and then have our application controllers extend it.
[ 57 ]
Using Views and Controllers
Creating a base controller Our application gained significant strength by switching from extending Kohana's Controller class to its Template Controller class. The Template Controller extends the Controller class, but adds the functionality to load and prepare a template view, making development easier. Using this same pattern, we can create a base controller for our app, have it extend the Template Controller, and in turn have our application controller extend our new base controller.
Time for action – Creating an abstract controller The case study application will have many controller actions that will all share the same layout, and many of the same features and view variables. We can house these common features in a controller that sits between the Template Controller and our extended controller.
1.
Create a file located at application/classes/controller/ named application.php.
2.
Open and add the following code to the application.php: template->content = ‹›; $this->template->styles = array(); $this->template->scripts = array(); } }
3.
Open the application/classes/controller/welcome.php and application/classes/controller/page.php files, and change the class declarations to extend Controller_Application instead of Controller_ Template.
[ 58 ]
Chapter 2
4.
In application/classes/controller/page.php, remove the following line from the action_about() and action_why_egotist() methods: View::set_global('site_name', 'Egotist Beta');
5.
In application/classes/controller/welcome.php, remove the following lines from the action_index() method: View::bind_global('site_name', $site_name); site_name = 'Egotist Beta';
6.
7.
Lastly, we will need to update our Welcome and Page controller one more time to have them extend the Application controller. Modify their class declarations as follows:
classController_Welcome extends Controller_Application and
Refresh your browser and view the output; the page should be exactly the same as it did in the previous section, only now the code is more organized and abstracted.
What just happened? By creating this controller as an abstract class, we are telling PHP that this controller is designed to be extended and not called directly. This allows us to put common code in this class, and have it accessible by any controllers that are its children or grandchildren. The next statement overrides the Template Controller's before() method. When we cover routing and request handling in more detail, we will get a more involved look into methods like before() and after(), but for now it is okay to think of the before() method as a sort of constructor, or a method that will be run before any other methods in the controller. Just like when we override the constructor of a parent class, it is very common to resolve the scope and inherit the functionality of the overridden method. By calling parent::before(), we are invoking the before() method of the Controller_ Template class. Next, we are setting the global view variable, $site_name, thereby removing the need for every controller or action method in our application from having this redundant code. By setting the site name variable here, we were able to remove calling it from the three action methods that are currently in existence in our application. Now, when we add future common variables, we can do so in our Application Controller and skip all the copy and pasting.
[ 59 ]
Using Views and Controllers
We then initialize the content view variable to prevent errors in our template when it attempts to print the variable. Lastly, we are creating two view variables for future use in our template. These are for our JavaScript and CSS files that we may want to use in our application to make it look better, enhance the user experience, or both. Right now, our view pages are very bland, using only the browser's default styles. It is about time to put some of our new knowledge to good use and make this application presentable.
Pop quiz – Controllers and views 1. To set a view variable and make it accessible to all View objects at time of render, Kohana provides which method: a. View::bind() b. View::set() c. View::set_global() d. Url::base() 2. Your application now has a few controllers. The site name is being set globally in the Application Controller, which extends what class? a. Controller_Template b. Controller c. Url::base() d. Controller_Welcome
Putting it all together Now that we have some experience creating views and controllers in Kohana, and have seen some of the patterns that can be used to make coding in Kohana easier, it is time to apply some of this knowledge and fix up the Egotist case study application. One of the first things we can do is create and link some stylesheets that will be used throughout our application to give our site some style. Since we created the template view variable to store an array of stylesheets, it should be easy to load the files from our template programmatically. Although we could directly add the links to our stylesheets in our template view file, there may be times when we have more than one template, and this again creates more than one place to copy and paste updates. By keeping the number of places necessary to update code at a minimum, our application will be easier to maintain. [ 60 ]
Chapter 2
Time for action – Adding stylesheets to template For our case study site, we will need to create and use a reset and a common CSS file for use on publicly viewable pages. For the reset.css file, we will use Eric Meyer's reset stylesheet that is freely available at http://meyerweb.com/eric/tools/css/reset/.
1.
Create a new directory in your site root named media. This is the place we will store images, CSS, and JavaScript files.
2.
In the new media directory, create a directory named css.
3.
Create a new file in media/css named reset.css, and use the reset available at http://meyerweb.com/eric/tools/css/reset/for its contents. It should look similar to this: html, body { height: 100%; background-color: #EEE; font-family: Verdana; } #container { width: 940px; margin: auto; min-height: 100%; background: url(‹../images/background.png›); } #header { width: 720px; margin: 0 auto 10px; } #logo { width: 200px; float: left; margin: 12px; } p#tagline { width: 275px; float: right; margin: 30px 90px 10px 10px; color: #999; font-size: 18px; font-weight: bold; [ 61 ]
margin: 0 auto 10px; padding-top: 18px; color: #666; } These CSS files and the images associated with them are available with the source code that accompanies this book.
4.
Open and edit the Application Controller located at application/classes/ controller/application.php, modifying the following code: $this->template->styles = array( ‹reset›, ‹common› );
5.
Open and edit the template view file located at application/views/template. php, modifying the following code inside the head tag: media/ css/.css» type=»text/css» media=»screen» /> <scriptsrc=»media/js/.js» />
[ 63 ]
Using Views and Controllers
6.
A refresh of our «why_egotist» page source shows our stylesheets are being added:
What just happened? We began by adding a couple of basic stylesheets to the application, organizing them in a folder named media in which will we be keeping our frontend assets. Because the focus of this book and exercise is on the Kohana framework, we will not spend a lot of time making our application very pretty, nor will we spend time reviewing the CSS, or testing for all edge use cases. The scope of this book can only cover so much. After creating the basic stylesheets needed to complete this chapter, we then added the names of these files to our Application Controller, passing them to the template view as an array in a view variable. By using our Application Controller to keep the list of stylesheets in one place, we can make it quick and easy to add, remove, or update the files we want to load. This will also allow us to make our application more advanced if needed, by overriding or having other means of creating the stylesheet array.
[ 64 ]
Chapter 2
In the template view file, we then created two foreach loops, iterating through the arrays for stylesheets and JavaScript files, and creating the corresponding HTML code necessary to load the files. Examining the code that is executed for each array value in our styles array, we see this line of code:
There is a similar line of code in the foreach block that iterates through the scripts array. This line of code should mostly make sense: it is a link tag, and it is loading the CSS file with the name of the array value concatenated to CSS. The link location is being passed, but it is starting with an unfamiliar bit of code:
This helper method, or static method provided by the framework to make coding easier, returns the base URL, generated using the values set in our bootstrap file. Another similar method is the URL::site() method, which returns the generated URL for our site. We will cover methods like this more in Chapter 4, where we will learn about Kohana's using classes and helper methods. Even though we have some stylesheets loading the structure of our application is lacking, even for an example site. Adding some basic structure, like a header and footer, is very easy using what we know about Kohana 3. Let's spruce up our view files.
Adding structure to view files For us to test and use the application, and eventually for our users, we will need some basic site structure and navigation. This will make testing and developing the site as we move forward easier. Also, now we can put some more of our new knowledge to work.
Time for action – Adding header and footer view files The stylesheets we added do no good if there is no document structure for it to style, and without basic features like a navigation menu, we cannot even test our site without constantly typing in the address bar. To fix this, we can add a header and footer view, and make some minor alterations to our template view.
1.
Locate and open the file located at application/views /template.php.
2.
Open the file for editing, and update the following code inside the body tag:
[ 65 ]
Using Views and Controllers
3.
In the application/views/ directory, create a folder named common.
4.
Inside the new application/views/common/ directory, create a file named header.php, and open it for editing. In this new file, add the following code:
Refreshing your browser (with the images from the code provided with this book) should yield the following results when you browse to http://localhost/ egotist/page/why_egotist:
Clicking on the navigation links should take you to the respective pages.
What just happened? The document structure provided the necessary tags for our CSS to have some effect. Apart from rendering the header and footer views from within the template view, most of the additional code in the template.php file should be self-explanatory; let's take a look at the header view render, which is new to us:
We have encountered the View::factory() static method a few times already, and know that it returns a new View object using the view file passed to it. Up until now, we have created the View objects in a controller, and have allowed the auto rendering to handle our view generation. As we can see, creating new views inside of a view script is just as easy. The header view file simply contains the site's logo, a tagline, and the navigation menu. As the application grows, or changes design, the logic in the controllers is completely separate from our views and template. Changing anything we have created so far only requires visiting one piece of code. This is the goal of DRY, or Don't Repeat Yourself. [ 67 ]
Using Views and Controllers
The footer is handled the same way as the header: we are creating a new View object with the footer view file, and then rendering it and printing the generated output. Once all this is put together, the end result is a working site, with three pages and very little custom code.
Have a go hero – Customize quickly and easily Now that the application is showing some signs of life, and we have a good grasp on how the template, controllers, views, and file system are all working together to deliver rich content with little code, we can now begin to customize our application's look and feel. By adding, updating, or modifying view variables and stylesheet code, we can make a lot of changes with little effort. Assume the case study site was to change names completely. How hard would it be to make the changes sitewide? Pick a new name for the application, and have it change in the title tag, nav menu, and so on, and see how easy it is to maintain a Kohana 3 application.
Summary When we began this chapter, our Kohana application was installed, but did not do much. After creating just a few files, and writing very little code, we have a working site with three pages. We have learned how to create and use view variables, create our own controllers, and extend Kohana's controller files to add more flexibility to our applications. By taking advantage of Kohana's Template Controller, we have eliminated almost all repetitive code, made updating and maintenance easier, and abstracted code to make reusability in future application features easy to implement. We also used our case study site to see real world examples of how to put all of our new knowledge into action. In our template view, we learned how to render other view files outside of the controller. We also applied basic styling and document structure to every page in the application by using the template and application controllers to do the heavy lifting. Now that we have learned how to create and use Controllers and Views in Kohana, let's take a more in depth look at how routing and request handling works in the framework. In the next chapter, we will learn about the way Kohana routes our requests, and how to use request handling and routing to make our applications more robust. We will also learn more about Kohana's cascading filesystem, the Heirarchy aspect of HMVC, and how Kohana works at runtime.
[ 68 ]
3
Routing and Request Handling What is HMVC, and what makes it so great? Frameworks utilizing the MVC design pattern are becoming very common in web development, but you may not be familiar with the Hierarchical aspect that is used in Kohana 3. Combine HMVC with a cascading filesystem, a powerful Routing class, and a new request flow, and it can all seem a bit daunting. This chapter focuses on answering all your questions about HMVC, routing, request flow, and the cascading filesystem in Kohana.
Now that we have installed Kohana and created our first views and controllers, it is time to take a look at the inner workings of the framework, so we will have the knowledge necessary to create web applications that take advantage of Kohana’s most powerful features. In this chapter, we will learn:
What HMVC is, and how it works
Kohana’s Request Flow
How to use the Request class
Routing in Kohana
How to use Routing in our applications
Advanced routing techniques
The topics covered in this chapter are important to every application build using Kohana. By the end of this chapter, we will have a strong understanding of the foundation of the framework, and a working knowledge of how it is applied in real work senarios. Let’s begin by exploring Kohana’s hierarchy.
Routing and Request Handling
Hierarchy is King in Kohana Kohana is layered in more ways than one. First, it has a cascading files system. This means the framework loads files for each of it’s core parts in a hierarchical order, which is explained in more detail in just a bit. Next, Kohana allows for controllers to initiate requests, making the application workflow follow a hierarchical design pattern. These features are the foundation of HMVC, which essentially is a cascading filesystem, flexible routing and request handling, the ability to execute sub-requests combined with a standard MVC pattern. The framework manages locating and loading the right file by using a core method named Kohana::find_file(). This method searches the filesystem in a predetermined order to load the proper class first. The order the method searches in is (with default paths):
Application path (/application)
Modules (/modules) as ordered in bootstrap.php
System path (/system)
Cascading filesystem As the framework loads, it creates a merged filesystem based on the order of loading described above. One of the benefits of loading files this way is the ease to overload classes that would be loaded later in the flow. We never have to, nor should we, alter a file in the system directory. We can override the default behavior of any method by overloading it in the application directory. Another great advantage is the consistency this mechanism offers. We know the exact load order for every class in any application, making it much easier to create custom code and know exactly where it needs to live.
This image shows an example application being merged into the final file structure that will be used when completing a request. We can see how some classes in the application layer are overriding files in the modules. As we will find later in the book, there could also be modules that could be loaded in place of core system files. This makes it easier to visualize how modules extend and enhance the framework by building on the system core. Our application then sits on top of the system and module files, and then can build and extend the functionality of the module and system layers. Kohana also makes it easy to load third-party libraries, referred to as vendor libraries, into the filesystem. [ 71 ]
Routing and Request Handling
Each of the three layers has five basic folders into which Kohana looks:
Classes (/classes) contain all autoloaded class files. This directory includes our Controller, Models, and their supporting classes. Autoloading allows us to use classes without having to include them manually. Any classes inside this directory will automatically be searched and loaded when they are used.
Config files (/config) are files containing arrays that can be parsed and loaded using the core method Kohana::config(). Some config files are required to configure and properly load modules, while others may be created by us to make our application easier to maintain, or to keep vendor libraries tidy by moving config data to the framework. Config files are the only files in the cascading filesystem that are not overloaded; all config files are merged with their parent files.
Internationalization files (/i18n) make it much easier to create language files that work with our applications to deliver the proper content that best suits the language of our users. Internationalization is beyond the scope of this book and will not be included in our case study site.
Messages (/messages) are much like configuration files, in that they are arrays that are loaded by a core Kohana method. Kohana:: message() parses and returns the messages for a specific array. This functionality is very useful when creating forms and actions for our applications.
View files (/views) are the presentation layer of our applications, where view files and template files live. We have created views in the previous chapter, and know what goes in this directory.
Request flow in Kohana Now that we have seen how the framework merges files to create a set of files to load on request, it is a good place to see the flow of the files in Kohana. Remember that controllers can invoke requests, making a bit of a loop between controllers, models, and views, but the frameworks always runs in the same order, beginning with the index.php file. The index.php file sets the path to the application, modules, and system directories and saves them to as constants that are then defined for global use. These constants are APPPATH, MODPATH, and SYSPATH, and they hold the paths for the application, modules, and system paths respectively. After the error-reporting levels are set, Kohana looks to see if the install.php file exists. This is the file we saw when we installed Kohana that ran some environment tests for us to confirm a proper installation. If the install file is not found, Kohana takes the next step in loading the framework by loading the core Kohana class. Next, the index file looks for the application’s Kohana class, first in the application directory, then in the system path. This is the first example of Kohana looking for our files before it looks for its own. [ 72 ]
Chapter 3
The last thing the index file does is bootstrap our application, by requiring the bootstrap. php file and loading it. You probably remember having to configure the bootstrap file when we installed Kohana. This is the file in which we set our base URL and modules; however, it is a bit more important than just basic installation and configuration. The boostrap begins by setting a some basic environment settings, like the default timezone and locale. Next, it enables the autoloader, and defines the application environment. This tells the framework whether our application is in a production or development environment, allowing it to make decisions based on its environment-specific settings. Next, the default options are set and Kohana is initialized, with the Kohana::init() method being called. After Kohana’s initialization, it sets up logging, configuration reading, and then modules. Modules are loaded defined using an array, with the module name as the key, and the path to the module as the value. The modules load order is listed in this array, and are all subject to the same rules and conventions as core and application code. Each module is added to the cascading file system as described above, allowing files to override any that may be added later when the system files are merged. Modules can contain their own init files, named init.php, that act similar to the application bootstrap, adding routes specific to the modules for our application to use. The last thing the bootstrap does in a normal request flow is to set the routes for the application. Kohana ships with a default route that loads the index action in the welcome controller. The Route object’s set method accepts an array that defines the name, URI, and defaults for the parameters set for the URI. By setting default controllers, actions, and params, we can have dynamic URLs that have default values if none are passed. If no controller or action is passed in the URI on a vanilla Kohana install, the welcome controller will be loaded, and the index action invoked as outlined in the array passed to the Route::set() method in the boostrap. Once all the application routes are set via the Route::set() method and init.php files residing in modules, Request::instance() is invoked, setting the request loop into action. As the Request object processes the request, it looks through routes until it finds the right controller to load. The request object then instantiates the controller, passing the request to the controller for it to use.
[ 73 ]
Routing and Request Handling
The Controller::before() method is then called, which acts much like a constructor. By being called first, the before() method allows any logic that need to be performed before a controller action is run to execute. Once the before method is complete, the object continues to load the requested functions, just like when a constructor is complete. The controller action, a method in the controller class, is then called, and once complete, it returns the request response. The action method is where the business logic for the request will reside. Once the action is complete, the Controller::after() method is called, much like the destructor of a standard PHP class. Because of the hierarchical structure of Kohana, any controller can initiate a new request, making it possible for other controllers to be loaded, invoking more controller actions, which generate request responses. Once all the requests have been fulfilled, Kohana renders the final request response. The Kohana request flow can seem like it is long and complex, but it can also be looked at as very clean and organized. By using a front controller design pattern, all requests are handled by just one file: index.php. Each and every request that is handled by our applications will begin with this one file. From there the application is bootstrapped, and then the controller designed to handle the specific request is found, executed, and displayed. Although, as we have seen, there is more that is happening, for most of our applications, this simple way of looking at the request flow will make it easy to create powerful web applications using Kohana.
Using the Request object The request flow in Kohana is interesting, and it is easy to see how it can be powerful on a high level, but the best way to understand HMVC and routing in Kohana is to look at some actual code, and see what the resulting outcome is for real world scenarios. Kohana’s Request object determines the proper controller to invoke, and acts as a wrapper for the response. If we look at the Template Controller that we are extending in our Application Controller for the case study site, we can follow the inheritance path back to Kohana’s template controller, and see the request response. One of the best ways to understand what is happening inside the framework is to drill down through the filesystem and look at the actual code. One of the great advantages of open sources frameworks is the ability to read the code that makes the library run.
[ 74 ]
Chapter 3
Opening the welcome controller located at application/classes/controller/ welcome.php, we see the following class declaration: class Controller_Welcome extends Controller_Application
The first thing we see in the base controller class is that it extends another Controller, and then we see the declaration of an object property named $request. This variable holds the Kohana_Request object, the class that created the original controller call. In the constructor, we can see that the Kohana_Request object is being type-hinted for the argument, and it is setting the $request object property on instantiation. All that is left in the base Controller class is the before() and after() methods with no functionality. We can then open our Application Controller that we created in the last chapter, located at application/classes/controller/application.php. The class declaration in this controller looks like this: abstract class Controller_Application extends Controller_Template
In this file, we can see the before() method loading the template view into the template variable, and in the after() method, we see the Request obeject ($this->request) having the response body set, ready to be rendered. This class, in turn, extends the Template Controller. The Template Controller is part of the Kohana system. Since we have not created any controllers in our application or modules the original template controller that ships with Kohana is being loaded. It is located at system/classes/controller/template.php. The class declaration in this controller looks like: abstract class Controller_Template extends Kohana_Controller_Template
Here things take a twist, and for the first time, we are going to have to leave the /classes/ controller/ structure to find an inherited class. The Kohana_Controller_Template class lives in system/classes/kohana/controller/template.php. The class is fairly short and simple, and it has this class declaration: abstract class Kohana_Controller_Template extends Controller
This controller (system/classes/controller.php) is the base controller that all requested controller classes must extend. Examining this class will let us see the Request class enter the Controller loop and the template view get sent to the Request object as the response. Walking through the Welcome Controller’s heritage is a great way of seeing how the Request object loads a controller, and how the parent classes all contribute to the request flow in Kohana. It may seem like pointless complexity at first, however, the benefits of transparent extension are very powerful, and Kohana makes the mechanisms work all behind the scenes. But one question still remains: How is the request object aware of the controllers and routes? [ 75 ]
Routing and Request Handling
Athough dissecting Kohana’s Request Class could be a chapter unto itself, a lot can be answered by looking at the contstructor in the system/classes/kohana/request.php file. The constructor is given the URI, and then stores object properties that the object will later use to execute the request. The Request class does have a couple of key methods that can be very helpful. The first is Request::controller(), which returns the name of the controller for the request, and the other is Request::action(), which similarly returns the name of the action for the request. After loading the routes, the method then iterates through the routes, determines any matches for the URI, and begins setting the controller, action, and parameters for the route. If there are not matches for optional segments of the route, the defaults are stored in the object properties. When the Request object’s execute() method is called, the request is processed, and the response is returned. This happens by first processing the before() method, then the controller action being requested, then the after() method for the class, followed by any other requests until all have completed. The topic of initiating a request from within a controller has arisen a few times, and is the best way to illustrate the Hierarchical aspect of HMVC. Let’s take a look at this process by creating a controller method that initiates a new request, and see how it completes.
Creating a profile page to render messages In our case study site, users will be creating text messages that will be listed on the site. These messages will be displayed on their public profile pages, and as the site grows, they may be viewed elsewhere. Although the first draft of the site will only show the messages on the user’s page, there could be growth where this content could be rendered in a mobile environment, in other pages, in widgets, or elsewhere. Listing the user’s messages is a core function of the application, after all. If we created the logic and view to display the messages inside the same controller action that renders the user’s profile page, reusing the code would be difficult, if not impossible. To prevent having to write this logic in several places inside the Egotist application, it is better to create a controller that handles the logic for listing messages for a user, and a view to list out the messages, and then have any pages that use the list to request it from within their corresponding actions.
[ 76 ]
Chapter 3
Time for action – Creating a profile page with messages Using the case study site to illustrate this process, we are going to add a profile controller to handle the public pages for our users. The default page for user’s public pages will be their profile page, with all of their recent messages and information. For now, we will just pass a fake username and some messages to test functionality.
1.
Create a new controller in our application/classes/controller directory named profile.php. This will be our Profile Controller that will be used to display user profiles and related items. In the newly created file, insert the following code: set(‹username›, ‹Test User›) ->bind(‹messages›, $messages); $messages = ‹This is ‹This is ‹This is );
array( test message one›, test message two›, test message three›
$this->template->content = $content; } }
2.
As we see, this controller’s default action is going to render a view in a directory named profile, with the view file named public. To create this view, we will need to make a folder called profile in the application/views directory.
3.
In our newly created application/views/profile directory, create and open a file named public.php. In this file, insert the following code:
Public Profile for = $username; ?>
Recent Messages:
= $message; ?>
[ 77 ]
Routing and Request Handling
4.
Open the page http://localhost/egotist/profile/, it should look like this:
What just happened? The only thing new here are the actual files. Creating a controller, action, and view are not new concepts to us, and we have experienced binding and setting view variables in previous examples. What we did create was another page, one that will be growing and becoming a very important part of the case study application. We are at one of the cross roads in application development, where we are best served by thinking about how the application could act in the future, and architect our code in such a manner to anticipate future needs while minimizing code complexity. We can assume a few things about how this page may act in the future. First of all, it will be rendering dynamic text for each user; therefore, it must have a way to retrieve data based on a particular user. The most common way of doing this is using a database, and retrieving the data from tables filled with user data. It is also very possible that the messages listed for a user may be viewed in other means, like mobile applications or widgets, and retrieving and listing these messages would be necessary for these other locations.
[ 78 ]
Chapter 3
Calling a new request inside a request Object-oriented programming has many great advantages, too many to list here, but code reusability and abstraction are paramount in leveraging this style of programming. The principle of Don’t Repeat Yourself (DRY) coding is easier to achieve when we write abstracted code that can be used throughout our projects without having to live in more than one place. Creating controllers that handle specific tasks that can be called directly, or requested from within other controllers or views, makes it much easier to create reusable, DRY code. Our current profile controller has a variable for our user’s name, and an array of their messages. Once a database is added, there will be code to retrieve this, and possibly more, information that will be passed to the view. Having the logic to handle all of this in the current controller action does not allow for the messages code to be reused in other parts of the site, and is not taking advantage of the HMVC architecture inherent in Kohana. To make our code more reusable and easier to maintain, we should create a controller and view to handle listing user messages.
Time for action – Creating a request within a request Having messages handled and rendered by a separate class allows us to abstract our code for more portability and reuse. Although the messages logic is very simple right now, only passing test data in an array, once a data store and more information tied to messages are introduced, the process could become more complex and needed in other parts of the application. To fix this, we can simply move the logic to a new controller class.
1.
Create a new controller in our application/classes/controller/ directory named messages.php. This will be our Messages Controller that will be used to display user profiles and related items. In the newly created file, insert the following code:
[ 79 ]
Routing and Request Handling $messages = ‹This is ‹This is ‹This is );
array( test message one›, test message two›, test message three›
As we see, this controller’s get_messages() action is going to render a view in a directory named profile, with the view file named messages. Create and open a file named messages.php in our application/views/profile directory. In this file, insert the following code:
= $message; ?>
3.
Open the page http://localhost/egotist/messages/get_messages/; it should look like this:
The Messages Controller is called and shows the listed messages per the view file. The get_messages action is passing the messages to the view. [ 80 ]
Chapter 3
4.
To incorporate this new controller into our existing application, we will need to alter our Profile Controller. Open the file located at application/ classes/controller/profile.php and modify it like this: set(‹username›, ‹Test User›) ->bind(‹messages›, $messages); $messages = Request::factory(‘messages/get_messages’)>execute()->response; $this->template->content = $content; } }
5.
Now that our Profile Controller is passing a rendered view into the messages view variable instead of an array, we will need to alter our profile view script. Open the view located in our application/views/profile directory named public.php, and change the code to look like this:
Public Profile for = $username; ?>
Recent Messages:
= $messages; ?>
6.
Open the page http://localhost/egotist/profile/; it should look like this:
[ 81 ]
Routing and Request Handling
What just happened? Again, the creation of a new controller and view should be familiar to us, but there are some things happening this time that are different from our previous experiences. In the Messages Controller, we have an index, or default action that is simply redirecting users back to the home page. As this controller only has one action we are using right now, any accidental navigation to this controller should default to returning users to the default controller and action for the application. We then created our first non-default action, action_get_messages(). When we viewed this controller action directly, we saw the messages being rendered to the browser, without any template surrounding the HTML. This is because the Messages Controller is not extending our Application Controller or the Template Controller, but is extending Kohana’s base Controller directly. This is because we want the rendered messages view to be diverse, and have the ability to be rendered inside of other views or controllers. When we altered our Profile Controller class, we removed the logic that is now living in the Messages Controller, and replaced the $messages view variable contents with this line of code: $messages = Request::factory(‘messages/get_messages’)->execute()>response;
Here, we are calling the factory method on the Request object, and telling it to request the Messages Controller’s get_messages action. This is the same request that would be fired if we browsed to http://localhost/egotist/messages/get_messages/. The execute() method then parses the request and loads the response into the Request object’s $request property, which we are calling via chaining. The same line of code could be written as follows for more clarity: $request = Request::factory(‘messages/get_messages’); $request->execute(); $messages = $request->response;
In our modified profile view, we replaced the foreach loop that iterated through the messages array and displayed the values with a simple echo statement that prints the results of the Messages request response. By using this pattern when writing our controllers, we will find our code to be easy to read, maintain, and reuse. As our applications grow, take on new functionality, and require maintenance, this style of coding will make it easier and faster to keep our applications up to date.
[ 82 ]
Chapter 3
Now that we have some experience with the Request object in Kohana, it is a good time to explore how we can use routes to keep our users, and data, flowing to the proper controller actions, and how to build dynamic applications using Kohana’s routing abilities.
Have a go hero – Create a request from within a view In the case study application, we have created a request from within our Profile Controller, and assigned the executed response to a view variable, which we are then outputting inside the profile view script. Although keeping logic in the controller is important, if no other manipulation is being done, simply content being rendered for output, it can just as well live inside the view. See if you can eliminate the request for messages from the controller, move it to the view, and not change the final output that is rendered when visitng the page.
Routing in Kohana If you remember, the bootstrap file comes preconfigured with a default route that follows a very simple structure: Route::set(‘default’, ‘((/(/)))’) ->defaults(array( ‘controller’ => ‘welcome’, ‘action’ => ‘index’, ));
This tells Kohana that when it parses the URL for any request, it first finds the base_url, and then the next segment will contain the controller, then the action, then an ID. These are all optional setgments, with the default controller and action being set in the array. We have taken advantage of this route with other controllers like our Profile and Message controller. When we visit http://localhost/egotist/profile, the route sets the controller to profile, and since no action or ID is explicitly defined in the URL, the default action of ‘index’ is used. When we requested http://localhost/egotist/messages/get_messages from within our Profile Controller, we also followed this route; however, neither defaults were needed, and the route asked for the Messages Controller and its get_messages action. In our Profile controller, we are only using one array of example messages to test functionality and the expected behavior of our application. When we implement a data store and have multiple users with profiles in our application, we will need a way to decipher which profile a user wants to see. Because the default route already has an available parameter for ID, we can use that to pass an ID to our Profile Controller’s index action, and have the messages controller then find the proper messages for that user. [ 83 ]
Routing and Request Handling
Time for action – Making profiles dynamic using ID Once a database is tied to our application, and more than one user has a profile, we will need some way of knowing which profile to display. A simple and effective way to do this is to pass a user ID in the route, and have our controller use that ID to find the right messages for the right user. Let’s add some more test data to our messages system, and use an ID to display the right messages.
1.
Open the Profile Controller in our application/classes/controller/ directory named profile.php. Since the action_index() method is the controller action that is called when a profile is viewed, we will need to edit it to look for the ID parameter in the URI like this: public function action_index() { $content = View::factory(‹profile/public›) ->set(‹username›, ‹Test User›) ->bind(‹messages›, $messages); $id = (int) $this->request->param(‘id’); $messages_uri = “messages/get_messages/$id”; $messages = Request::factory($messages_uri)->execute()>response; $this->template->content = $content; }
2.
Now, we are retrieving the ID from the route and passing it along in our request to the Messages Controller. This means that class must also be updated. Open the messages.php file located in application/classes/ controllers/ and modify its action_get_messages() method as follows: public function action_get_messages() { $id = (int) $this->request->param(‘id’); $messages = array( 1 => array( ‘This is test ‘This is test ‘This is test ), 2 => array( ‘This is test
message one for user 1’, message two for user 1’, message three for user 1’
message one for user 2’, [ 84 ]
Chapter 3 ‘This is test message two for user 2’, ‘This is test message three for user 2’ ) ); $messages = array_key_exists($id, $messages) ? $messages[$id] : NULL; $this->request->response = View::factory(‘profile/messages’) ->set(‘messages’, $messages); }
3.
Open the page http://localhost/egotist/profile/index/2/. It should look like this:
Browsing to http://localhost/egotist/profile/index/1/ will show the messages for user 1, i.e., the test messages placed in the message array under key 1.
What just happened? At the very beginning of our index action in our Profile Controller, we set our $id variable by getting the ID parameter from the route. Since Kohana has parsed our route for us, we can now access these parameters via the request object’s param() method. Once we got the ID variable, we then created and executed the request for the message controller’s get_messages action, and passed the ID to that method for it to use. [ 85 ]
Routing and Request Handling
In the Message Controller, we used the same method to extract the ID from the request, and then used that ID to determine which messages from the messages array to display. Although this works fine for illustrating routing for these two users, the code is far from ready, even without a data store or real user data, but it does show how the parameters can be read and used. Because most of the functionality in the controller will be replaced with our database and more precise data being passed around, we can overlook the incompleteness of the current controller actions, and begin looking at creating a URL that is better looking than http:// localhost/egotist/profile/index/2/ for finding a user profile by ID.
Creating friendly URLs using custom routes Consider how nice it would be if our users could browse to a profile without putting ‘index’ in the action portion of the URI, like this: http://localhost/egotist/profile/2. This looks much more pleasing, and is more in line with what we would like our URLs to look like in web apps. It is in fact very easy to have Kohana use a route to remove the index action from the URI. Routes not only make our URLs more pleasing and descriptive, but they make our application easier to maintain in the long run. We have more control over where our users are being directed from how the URL is constructed, without having to create controller actions designed to handle routing.
Time for action – Creating a Custom Route So far, we have been using the default route that is in our application bootstrap. As our application grows, so will the number of available ‘starting points’ for our user’s requests. Not every controller, action, or parameter has to comply with the default route, and this gives us a lot of flexibility and freedom. We can add a custom route to handle user’s profiles by adding it to our bootstrap.php file.
1.
Open the bootstrap.php file located in application/ directory and modify the routes block so it looks like this: /** * Set the routes. Each route must have a minimum of a name, a URI * and a set of defaults for the URI. */ Route::set(‘profile’, ‘profile/’) ->defaults(array( [ 86 ]
Now, we can view the profile pages without having to pass the index action in the URL. Open http://localhost/egotist/profile/2 in a browser; it should look like this:
Browsing to profiles with a more friendly URL is made possible through Kohana’s routes.
[ 87 ]
Routing and Request Handling
What just happened? By setting routes using the Route::set static method, we are essentially creating filters that will be used to match requests with routes. We can name these routes; in this case we have one named default, and one named profile. Kohana uses the second parameter in the set() method to compare against the requested URI, and will call the first route that matches the request. Because it uses the first route that matches the request, it is very important when ordering route definitions. If we put the default route before the profile route, the profile route will never be used, as the default route would always match first. Because it looks for a match, it does not use discretion when determining the right route for a request. So if we browse to http://localhost/egotist/profile/index/2, we will be directed to the default route, and get the same result. The default route may not be available for all the routes we create in the future, so create routes that are as explicit as we can for our needs. Right now, our application assumes any data that is passed after a controller segment named ‘profile’ must be the ID for which we are looking. In our current application setup, we only need digits. If a user passes data into the URL that is not numeric for the ID parameter, we do not want it to go to that route. This can be accomplished easily inside the Route::set() method.
Making routes explicit Having routes that are properly defined means having routes that are explicit so they match only the request to which they are designed. Kohana::set() offers a third argument that accepts an array of URI segment keys and regular expression values. The URI segment keys are enclosed in angle brackets (<…>). The method then applies these regex filters to the segments when determining if there is a match.
Time for action – Adding regex to a route Modifying our profile route with a regular expression will allow Kohana to send requests only to that route where the ID is a number. Open our bootstrap.php file, located in application/directory, and modify the route’s set method for the profile route so it looks like this: Route::set(‘profile’, ‘profile/’, array(‘id’ => ‘[0-9]+’)) ->defaults(array( ‘controller’ => ‘profile’, ‘action’ => ‘index’, ));
[ 88 ]
Chapter 3
What just happened? The regular expression [0-9]+ will only match one or more numeric characters, no matter how long. This works fine for requiring that the route only match requests where the URI has ‘profile’ in the first segment, and a numerical value in the second segment. While this is great for finding a user by their ID, and the URL that a user would use to find the profile is nicer than it was before, it is still not as descriptive as it could be. It would be nice if the user’s name were also allowed to be passed into the path, even if the application had no use for it, it would add more description to the possible destination.
Allowing additional data in a URI Apart from being easier to read and type, friendly URLs can also help to describe the content on the page for users and search engines. It is not uncommon to pass additional data in the route to better describe the page, even though it is optional and not used by the application itself. Unfortunately, if we wanted to link to a profile with the user’s name appended to the URI, it would not find the route we have defined. If we linked to http://localhost/egotist/ profile/2/some-users-name, we would get an application error, as there is not a route that is defined that matches this scheme, and the default route will look for a method named action_2 in the profile controller, as defined in the default route in our bootstrap file.
Time for action – Allowing additional segments in a route By making a simple modification to our profile route, we can match any URI that has the basic requirements for our route, and allow some optional data to be passed. Although we will ignore this data in our controllers, other places, like search engines, may find it very useful. Open the bootstrap.php file located in application/directory and modify the route’s set method for the profile route so it looks like this: Route::set(‘profile’, ‘profile/(/)’, array( ‘id’ => ‘[0-9]+’, ‘optional’ => ‘.*’) )->defaults(array( ‘controller’ => ‘profile’, ‘action’
=> ‘index’,
));
[ 89 ]
Routing and Request Handling
What just happened? By adding the third segment to our URI structure for the profile route, we are allowing additional data to be passed after the ID, and still match the route we are wanting. If you notice, we also added another regular expression that will match any character, 0 or more times, to the third argument’s array, with a key referencing the new optional segment. Segments are defined as optional when they are enclosed in a set of parenthesis. Without this regular expression, only one additional segment is allowed, but with the regex, we can now pass all the additional segments of data we wish, and still match this route. A quick glance at http://localhost/egotist/profile/2/some-user/more-data/ even-more-stuff still shows our profile page for user number 2:
Using subdirectories with routes The controller directory can quickly become overrun with files, making an otherwise wellorganized project become cluttered and hard to navigate. Besides being able to set default controllers and actions for a route, we can also define the directory that a route’s controllers live in. This can make organizing our files much easier.
[ 90 ]
Chapter 3
Time for action – Using Subdirectories and Routes We can assume there are going to be more than the five files in our classes/controller directory as our application grows. By organizing our controller classes in subdirectories, and then using our routes to define the location of our classes, we can better organize our application and prepare for future growth. We can add a directory to our route, and then update our controller’s class declarations to better organize our files.
1.
Open the bootstrap.php file located in application/directory, and modify our profile route so it looks like this, adding the user-messages route below our profile route: Route::set(‘profile’, ‘profile/(/)’, array( ‹id› => ‹[0-9]+›, ‹optional› => ‹.*›)) ->defaults(array( ‘ directory’ => ‘user’, ‘controller’ => ‘profile’, ‹action› => ‹index›, )); Route::set(‘user-messages’, ‘messages//(/)’, array( ‘id’ => ‘[0-9]+’, ‘optional’ => ‘.*’)) ->defaults(array( ‘directory’ => ‘user’, ‘controller’ => ‘messages’, ‘action’ => ‘index’, ));
2.
Next, we will need to create the user subdirectory inside our application/ classes/controller/ directory.
3.
Inside our new application/classes/controller/user directory, move the Profile Controller inside of it, and change its class declaration to match its new location: class Controller_User_Profile extends Controller_Application
[ 91 ]
Routing and Request Handling
4.
Inside the new application/classes/controller/user directory, move the Messages Controller inside of it, and change its class declaration to match it’s new location: class Controller_User_Messages extends Controller {
5.
A quick reload of http://localhost/egotist/profile/2/someuser shows everything is working fine, as nothing has changed.
What just happened? Inside our route named ‘profile’, we added an additional array key for directory in the defaults array settings, and assigned it the value of ‘user’. This tells Kohana that the controllers for this route will need to be located inside a classes/controller/user directory, and will need to start with the class name Controller_User, based on the framework’s naming conventions for controllers. We also had to create an additional route for our Messages Controller, since it serves users also, it makes sense to move it into this subdirectory also. Now, when the request for messages is made inside the Profile Controller, Kohana will know to find it in the user subdirectory. After moving our Profile and Messages Controllers inside a subdirectory named user, we had to open the files and change their class names to include their path, changing the Controller_ prefix to Controller_User. Now, whenever a request has a URI with a first segment of profile or messages, Kohana will attempt to match it to one of our custom routes.
Pop quiz – Understanding Routes and the Kohana Filesystem 1. The Route::set method is used to create filters for the request with which to be matched. The method takes three arguments. In what order are they accepted? a. b. c. d.
Route Name, URI, Array of Regular Expressions Array of Regular Expressions, URI, Route Name URI, Array of Regular Expressions, Route Name Route Name, Array of Regular Expressions, URI
2. Kohana’s cascading filesystem has three layers that it merges together when processing a request. What order are these layers merged in order of importance? a. Modules, System, Application b. Application, System, Modules c. Application, Modules, System d. System, Modules, Application [ 92 ]
Chapter 3
3. When adding routes to your application layer, they need to be created using the Kohana::set() method and should be located inside which file? a. index.php b. application/bootstrap.php c. application/classes/init.php d. application/views/template.php
Summary Kohana’s request flow and routing is one of the fundamental aspects of the framework, and is very important to understand as you move forward as a Kohana developer. By creating and using some custom routes, you were able to see how real world scenarios can be handled gracefully by Kohana. You were also able to add to the case study site to see exactly how these principles are related to executable code. Even though most of the code added in this chapter was example data, when it comes time to adding information from the database, you will have the understanding necessary to manipulate the routes and requests to properly use that data. After this chapter, you should have a firm grasp on creating, using, and now requesting Kohana’s Controller classes. In the next chapter, we will look at some of the helper and service-oriented classes provided in Kohana and take a look at extending their functionality, and creating a few of our own.
[ 93 ]
4
Working with Helpers and Classes In this chapter, we will learn to utilize Kohana's helper classes to make our code faster to write and easier to use. By using some of the more commonly used classes, our application will be easier to read and maintain. In this chapter, we will learn:
Autoloading classes In Kohana
An overview of helpers
Working with dates and times using the Date helper
Rendering and filtering HTML with the HTML, Inflector, and Form classes
How to keep things secure using the Encrypt and Cookie classes
Using the Arr helper to manipulate arrays
How to create and parse RSS feeds in one line
How to extend the core helper classes, and how to create your own
In previous versions of Kohana, and in some other MVC frameworks like CodeIgniter, helpers are defined classes that have their own directory. Now, helpers are treated as first class citizens, and can be thought of as service classes, implementing design patterns to make code more reusable. In this chapter, we will get familiar with some of the helper methods that come included with the framework, learn how to extend core classes to add functionality, and how to create our own helpers for our projects.
Working with Helpers and Classes
Autoloading classes By using the autoloading capability of PHP5, we are no longer required to list all the include() and require() statements that are necessary in older applications. Kohana uses the __autoload() magic method along with the cascading filesystem to create an easy means for loading classes. In our bootstrap file (application/bootstrap.php), we can see the following line of code: spl_autoload_register(array('Kohana', 'auto_load'));
This registers the Kohana::auto_load() method as the default autoloader for our application. We can create custom autoloaders; however, for most projects this will not be necessary. When a class is called that we have not loaded already, this autoload method will be invoked. On a high level, Kohana::auto_load() is a fairly simple method that applies the filesystem rules to the class being loaded. In short, the underscores are converted to slashes, the string is converted to lowercase, and the file is searched for in the classes/ sub-directory of Kohana's cascading filesystem. Because of the cascading filesystem, this makes inheritance very easy, and therefore extending the functionality of the framework becomes transparent. When we begin to cover extending the core functionality of Kohana we will not be required to load the parent classes, manually instead we will simply call them in our class declaration and move on.
Introducing helpers In previous versions of Kohana, and in other MVC frameworks like CodeIgniter, helpers are separated from other library code, and are given their own directory in the filesystem. There is no longer a restriction on where these classes live or are called, and they are auto-loaded just like any other class in Kohana. We will refer to methods that provide the functionality of helpers in the traditional method, being formatting and providing additional functionality to standard features, as helpers in this chapter. Unlike module code, and other library code, helpers are classes that contain static methods (in most cases). Without having to create instances of each class, they become very easy to implement and 'help' us do common tasks like formatting or performing tasks that live in a more global namespace.
[ 96 ]
Chapter 4
Most helper classes contain several methods that all share a common theme. In the user guide's API Browser, there are many classes listed, and a majority of them are there to assist, or help, us with our tasks. Each class has several methods, all dedicated to working with something associated with the class' name. For instance, the Date Class has many methods that are designed to work with dates and time. There are a few helper classes that are commonly used in many applications, and we will take a look at how to use these more common classes in more detail.
The Date class Working with dates and times in PHP can be difficult, despite all the native functions and classes that are included with the language. Some common tasks web developers face every day are listing years for select boxes, finding the range between two dates or times, and manipulating information based on time zones. Kohana's Date Class provides many methods to help us when manipulating dates and times in our applications. A common task is having to list the number of days in a particular month. Determining how many days are going to be in February of 2019 could take several lines of code, but Kohana makes it easy: $days = Date::days(2, 2019);
We can then use this to create a select box for date selection, or count the array for number of days, and move on, not having to write functionality to do the hard work for us. The months(), years(), seconds(), hours(), and minutes() methods all work in a similar fashion, and each contain additional options that can be found in the userguide. The first conversion method in the Date Class is a function to convert 12-hour formatted time into a 24-hour format. The Date::adjust() method accepts two parameters, the first is the hour as an integer, and the second is a string that accepts 'am' or 'pm'. Let's take a look:
The ampm() method simply determines whether a 24-hour-based time is AM or PM in a 12-hour format: [ 97 ]
Working with Helpers and Classes
Social networking sites have made 'fuzzy' time stamps very popular. Instead of displaying the exact time and date for new messages, they commonly use timestamps that are more human readable, like "3 minutes ago" or "moments ago". Kohana makes it very easy to use this in our applications:
And so on. For a complete list of all the possible output text options, take a look at the code in /system/classes/kohana/date.php in the fuzzy_span() method. The method has a long list of if..elseif blocks that sets the return text. The Date class has a few other methods at which you may want to take a look. Like all of the helper methods, you are encouraged to look in the API Browser at the available methods, and take a look at the source code.
Have a go hero – Customizing fuzzy date text You may create applications in the future that will use the fuzzy dates, but decide that you do not want to use the text that is currently used in the framework. Because you want to keep the rest of the functionality of the Date class, you decide to override the fuzzy_span() function by extending the Date class inside your application. Based on what you learned in the last chapter, how would you create your custom messages while preserving the rest of the Date class's functionality, without altering any code in the /system/ directory?
The HTML Class The HTML class provides a programmatic syntax for rendering and converting HTML elements. These helper methods can be very useful when we are passing data into form elements, and would rather have a nice interface in the place of heavy concatenation. Most of the standard HTML elements are represented. There are a few methods designed specifically for converting strings for use in HTML. These methods are often used in conjunction with other code; however, they can be used standalone also.
[ 98 ]
Chapter 4
The HTML::chars() method Converting user-generated content into HTML entities is a good defence against cross-site scripting (XSS) vulnerabilities. This method runs PHP's htmlspecialchars() function on the passed value with ENT_QUOTES, the configured character set, and sets double-encode to true. alert("xss"); script>'); ?>
By passing the above JavaScript through the HTML::chars() method, the HTML output will look like this: <script type="javascript">alert("xss"); script>
This will print the above text, but will not execute the script. Any time untrusted data is to be displayed in an application, we should always run the HTML::chars() method. This includes any data that has been input by a user.
The HTML::obfuscate() method Sometimes, we want to display text in HTML, but we do not want the output to be easily read by scripts, like spiders and crawlers. This is often used for SPAM prevention. One method for accomplishing this is to obfuscate the text using HTML entities and Hex character encoding:
The above code will output something like this: Some Member Name
One of the most common items that we obfuscate as developers are email addresses, and Kohana has a method especially for email obfuscation.
The HTML::email() method This method allows us to easily pass an email address and get back a randomly generated string that will read as an email, but prove more difficult for data miners.
The above code will output something similar to this: someone@somewhere. x63;om
[ 99 ]
Working with Helpers and Classes
This will be read by the browser and displayed as readable text, but will be obfuscated in the source. In other words, our user will see "[email protected]" but when we view source, the HTML entities and Hexadecimal character encoded string will be in it's place. If a crawler were searching for email addresses, it would come up empty handed. Because the HTML::obfuscate()and HTML::email() methods randomly determine which characters are encoded, and what way they are encoded, the complexity for accurately scraping our pages becomes a bit higher. The rest of the HTML class' helper methods deal with the rendering of elements. They provide a more elegant solution to concatenating long strings in our views to create HTML. These can also be used in our application code, making it possible to incorporate rendered elements into a view variable for output in the presentation layer.
The HTML::anchor() method The anchor tag is used in virtually every web application. To create an anchor using the HTML class, we can simply do this:
If added into our case study site, this would create the following HTML: Welcome Page
The first parameter of HTML::anchor() is the URL for the link, the second an optional title, and the third allows for attributes to be assigned to the element. If no title is passed, the link will be used in its place. There is also an optional fourth parameter that will accept the protocol for the link. To pass attributes to the element, we can do something like this: 'foobar' )); ?>
The HTML helper has a very similar method to anchor() named file_anchor(). The only difference between these two methods is file_anchor() will use the basename(), or file name, of the file as the default link title.
[ 100 ]
Chapter 4
The HTML::image() method Next to links, images are another very common element in web applications. The HTML::image() method returns a rendered img element. There are three parameters: the first is the file location, the second is an array of attributes, and the last is a tool that determines whether the index file should be prepended to the URL if a relative path is provided. The last two parameters are optional. 'Alt Text')); ?>
The above code will output this HTML:
The image(), script(), and style() methods all work exactly the same, with the latter two creating <script> and <style> elements with attributes.
The HTML::mailto() method The HTML::mailto() helper is only slightly different, in that it takes an email address as the first parameter, then an optional title and attributes. 'email_addy')); ?>
The above code will return something like this: Email Someone
This method simply runs the HTML::email() method and then wraps the result in a mailto: link. Again, because the obfuscation method is random, your results may vary slightly in the obfuscated portion of the email address.
The Inflector Class Manipulating words as strings is the focus of the inflector class. This can come in handy when calling methods programmatically; changing a string from singular to plural for multiple reasons (say to get a database name from an object name), and to convert dashed, underscored, and camelized text to a human readable form. Changing text from almost any form to camel case can be accomplished with Inflector::camelize(): [ 101 ]
Working with Helpers and Classes
The decamelize() method takes camelized text and returns as normal text with spaces:
The underscore() method is similar to camelize, but instead of creating camel case, it creates underscored text:
The humanize() method takes dashed or underscored text, and turns it into human friendly text:
Although changing text to make it easy for programming is nice, being able to change text for human consumption is even nicer. The amount of intelligence needed to change words that can be read by humans is much higher than simply manipulating spaces, underscores, and dashes. To change words from plural to singular, simply pass them into the Inflector::singular() method:
If the text is already singular, or is uncountable, it will remain unchanged. The opposing method to singular() is plural():
As we can see, patience did not change. This is because it is considered 'uncountable' in that there is no 'patiences'; the plural of patience is patience. To determine whether a word is uncountable, Kohana provides the Inflector::uncountable() method:
The list of uncountable words can be found in /system/config/inflector.php. In this file there is an array of uncountable words, and an array of irregular words.
[ 102 ]
Chapter 4
The URL class Working with URLs can be tricky business. There are issues with characters, character types, paths, and protocols. These can all combine to create many headaches when developing web applications, however, the URL class makes many of these chores easy to accomplish with very little code. A common task for developers is to create URL friendly slugs to insert into URLs, and like any good helper, the URL class makes this simple. The next three methods in the URL class are designed to create URLs and query strings.
The URL::title() method The URL::title() method is great for creating slugs from page titles, and then including the slug in links to the page. Even if the slug is not used for database retrieval or any other purpose, it helps make the URL more readable, lets users see the title in the URL, and most importantly, it is great for search engine optimization.
The URL::title() method first appears like it should be a member of the HTML class. Upon further inspection, we will notice this method takes text conversion another step by ensuring the rendered result is safe for inclusion in a URL. When we set up our application's bootstrap file, we set the base URL that Kohana will use when creating a new URL.
The URL::base() method The URL::base() method is used to create the base URL for our application. By using this method, any time we need to update our base URL site wide, we only need to change it in our bootstrap file.
The URL::base() method accepts two optional parameters: the first is a bool to include the index file, and the second is the protocol we want to use when rendering the URL. In the case study site, egotist, we set the index file configuration as an empty string, so there will be nothing to pass. Sometimes URL redirection is not available, and including index.php in URLs may be unavoidable. If we need to pass a file as our front controller, we could do so in the first parameter.
[ 103 ]
Working with Helpers and Classes
By passing https in the second parameter, the protocol for the URL is set. This is great to use on links we know need to go to secure pages. Building on the functionality of the URL::base() method is URL::site(). This method accepts a URI as a string and the desired protocol. Left empty, it will return the same value as URL::base().
The last helper method in the URL class is used to help create query strings for our URLs.
The URL::query() method This method builds a GET query string that is ready to be attached to any URL. 12, 'keyword' => 'narwal')); ?>
The above code will return: ?page=12&keyword=narwhal
This is very helpful when creating URLs or links where a GET method is used to generate page information. In one line, we can either create or pass an array into the method, and get a properly formatted, URL safe query string in return. If you want to reuse or overwrite the current query string values, simply set the second parameter of the URL::query() method to true. The URL class is short and sweet, but it is probably the most used helper in a Kohana application. If we use URL::base() and URL::site() for every link you create and resource we load, our programs will be easier to maintain; using URL::title() and URL::query() will make our URLs safe, friendly, and dynamic.
The Arr (Array) Class This helper adds additional array helpers that PHP does not have. Although it is relatively easy to accomplish many tasks using PHP's native array functions, the Arr class provides some very useful additions that have good use cases in web applications. [ 104 ]
Chapter 4
It is very common for developers to set a default value for a variable, and then determine whether the variable was set via $_GET or $_POST (or other methods), and then replace the default value with a new one.
The Arr::get() method The Arr::get() method gets the value for a single array key from a specified array, and if that key does not exist, it will use the default value. This method accepts three parameters: the first is the array being searched, the second is the key that is being retrieved, and the last is an optional default value. If no default value is given, then NULL is used. A good use case for Arr::get() would be setting data that could be entered by a user via a form. We could create a variable based on the input from a POST request, and if none is given, set the variable to null. This will prevent PHP warnings is no value is set.
Alternatively, we could use this method to set the default value if the key does not exist. A common use case for this may be to have the page passed as a GET parameter in the URL, and if none is given to default to page 1.
This method saves a few lines of code every time a value is needed from an array, and if none is given, to have an elegant way to handle default values.
The Arr::extract() method The extract() method does the same thing as get(), but extract() accepts multiple keys to be retrieved from the haystack.
This line of code would assign a new array to $page_info that contains two keys: 'page' and 'slug'. If the $_GET array contains these keys, they will be set the values from the $_GET array, otherwise they will be the defaults value in the third parameter. If no defaults were given, NULL would be assigned in its place.
The Arr::pluck() method To get the values from a key from multiple arrays can be accomplished with the Arr::pluck() method. This accepts a list of arrays (an array of arrays) and then searches each of them for a key, and returns an array of all the values. To conceptualize this, look at the following example:
Working with Helpers and Classes $array_1 = array( 'first_name' => 'Joe', 'last_name' => 'User' ); $array_2 = array( 'first_name' => 'Jason', 'last_name' => 'Straughan' ); $array_3 = array( 'first_name' => 'Ken', 'last_name' => 'Jones' ); $arrayList = array($array_1, $array_2, $array_3); $first_names = Arr::pluck($arrayList, 'first_name'); ?>
This will create an array $first_names containing three values: 'Joe', 'Jason', and 'Ken'. This is a very helpful method to use when we need to merge several arrays and get similar data from each.
The Arr::merge() method Speaking of merging arrays, the Arr::merge() method does exactly that. Anytime we need to combine two or more arrays into a single array, merge() is ready. When using Arr::merge() there are a few factors of which we need to be aware. First, associative arrays are merged recursively. Secondly, associative values are replaced and new ones are preserved if none exists. The nice thing about this method is that all keys are maintained. 1, 'favorite_foods' => array( 'bacon', 'cheeseburgers', 'pizza' ) ); $array_2 = array( 'id' => 2, 'favorite_foods' => array( 'bacon', 'narwhal stew' ) ); [ 106 ]
A var_dump() of $merged yields the following: array(2) { ["id"]=> int(2) ["favorite_foods"]=> array(4) { [0]=> string(5) "bacon" [1]=> string(13) "cheeseburgers" [2]=> string(5) "pizza" [3]=> string(12) "narwhal stew" } }
As we can see, the 'id' key now has the value from the second array, but the 'favorite_ foods' array now contains a merge of both arrays with no duplicates. This can be used when combing an array of default values with a set of optional values, among other things.
The Arr::overwrite() method If we would like to merge two arrays, overwriting the values from one array with the other's, there is another method to handle that. The Arr::overwrite() method accepts two arrays, and replaces any values present in the first array with the values from the second. The main difference between this and Arr::merge() is Arr::overwrite() does not add keys from the second array that do not exist in the first. '1', 'keywords' => null, 'sort_order' => 'asc', 'order_by' => 'name' ); $new_values = array( 'page' => '12', 'keywords' => 'narwal, bacon', 'order_by' => 'rank', 'username' => 'jdstraughan' ); $values = Arr::overwrite($defaults, $new_values); ?> [ 107 ]
Working with Helpers and Classes
This will create an array assigned to $values that contains the following data: array(4) { ["page"]=> string(2) "12" ["keywords"]=> string(13) "narwhal, bacon" ["sort_order"]=> string(3) "asc" ["order_by"]=> string(4) "rank" }
Here, the $new_values are replacing the values for any keys that are present in the $defaults array. This is a good use case we may use in many applications, where our class has default properties and based on GET, POST, or route input these values must be overwritten. Also, notice that the 'username' key was not added to the $values array. This key is not present in the first array, and is therefore not included in the new array.
The Arr::flatten() method We can also convert multidimensional arrays to single-dimensional arrays in one line using the Arr::flatten() method. 1, 'favorite_foods' => array( 'bacon', 'cheeseburgers', 'pizza' ) ); $flat = Arr::flatten($multi); ?>
This will create an array assigned to $flat that looks like this: array(4) { ["id"]=> int(1) [0]=> string(5) "bacon" [1]=> string(13) "cheeseburgers" [2]=> string(5) "pizza" }
The flatten() method will preserve associative keys, and create indexed keys for nonassociative values. In this case, ID was preserved and the values for favorite_foods were assigned indexed keys.
[ 108 ]
Chapter 4
The Arr::unshift() method PHP has a great array function named array_unshift() that prepends a new value to an array. This can be very handy when we need to add some information to the beginning of an array. Arr::unshift() provides us with some improved functionality in that we can specify the key and the value to be prepended to the array. If the key exists, it will be replaced by the new value, and the size of the array will remain the same. To use Arr::unshift(), pass the array we are unshifting as the first parameter, the key as the second, and the value as the third. 'Jason', 'last_name' => 'Straughan', 'username' => 'jdstraughan' ); $user = Arr::unshift($user, 'id', 42); ?>
The $values variable will now contain the following array: array(4) { ["id"]=> int(42) ["first_name"]=> string(5) "Jason" ["last_name"]=> string(9) "Straughan" ["username"]=> string(11) "jdstraughan" }
Here, we can see that the 'id' key is added to the beginning of the array. $user['id'] = 42;
The above code would add the key/value pair to the end of the array natively.
The Arr::map() method Anther example of a PHP function with similar functionality in Kohana is array_map(). This function in PHP allows us to run a callback function against an array's elements and returns an array of the results. The Arr::map() function is similar, but it runs the callback function recursively; unlike array_map(), it only will run against one array. 42, [ 109 ]
Working with Helpers and Classes 'userinfo' => array( 'first_name' => 'Jason', 'last_name' => 'Straughan', 'username' => 'jdstraughan' ) ); $big_user = Arr::map('strtoupper', $user); ?>
This will create an array $big_user with all values being passed through strtoupper(). array(2) { ["id"]=> string(2) "42" ["userinfo"]=> array(3) { ["first_name"]=> string(5) "JASON" ["last_name"]=> string(9) "STRAUGHAN" ["username"]=> string(11) "JDSTRAUGHAN" } }
The Arr::is_array() method Another great method in the Arr class is the is_array() method. The Arr::is_array() method takes PHP's is_array() method a step further, and checks if the variable in question is an array or is an object that implements PHP's Traversable interface. Although the latter is not actually an array, it behaves very much in the same way.
Not only can we check if a variable contains an array, with the is_assoc() method, we can see if it is an associative array. 12, 'keyword' => 'narwhal')); // TRUE ?>
The Arr::range() method The last Array function we are going to cover is the Arr:range() method. This function simply creates an array of a range of numbers, and can be used for creating test data, select boxes, or any time we need an array full of an ordered set of numbers. The first parameter is the increment, or step, that should be applied, and the second is for the maximum value of the range. The increment must be greater than zero, and it will also be the first number in the range. [ 110 ]
Chapter 4
The above code will create an array with the following elements: array(10) { [1]=> int(1) [2]=> int(2) [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [7]=> int(7) [8]=> int(8) [9]=> int(9) [10]=> int(10) }
Here we see it starting at 1, and increasing through 10. To count by twos, simple change the first value:
Now, we can see that the range starts with 2, and contains all the even numbers through 10: array(5) { [2]=> int(2) [4]=> int(4) [6]=> int(6) [8]=> int(8) [10]=> int(10) }
As we can see, the Arr class is a nice addition to the functionality inherent in PHP. As far as helper classes in Kohana go, Array is one of the most powerful. Proper usage of these methods can save hours of programming, and leave us with more time to write great applications.
The Cookie class Working with cookies just got a lot safer, and a lot easier. The Cookie method is short and sweet, and makes working with cookies fun again. Tampering with cookies is an all-time favorite pastime for most hackers and can present several security risks to our application. To help prevent attacks on our sites, encrypting our cookies and validating their contents is critical. By using Kohana's Cookie class to set and retrieve our cookies, we have access to one-line commands that handle all the encryption and validation intuitively, without even having to think about it. The Cookie::set() method is very straightforward. The first parameter is the name of the cookie, the second is the value, and the third, optional, parameter is the expiration in seconds. It should be noted here that PHP's setcookie() function accepts a UNIX timestamp, where the Cookie::set() method accepts the cookie's lifetime in seconds:
The above code will create a new cookie with the name 'username' and a value that has an encrypted signature prepended to the value. This way, if the value is manipulated, the signature will not match and Kohana will not allow the value to be retrieved via the Cookie class. [ 111 ]
Working with Helpers and Classes
Printing out the contents of the $_COOKIE global variable's value for our username cookie will reveal the contents of the cookie: 84330a7f8b2fc797f215979f83ad38b4dbe81f53~jdstraughan
If someone were to alter the jdstraughan portion of the cookie, it would not pass the validation in Cookie::get(), and, by default, NULL would be returned in place of the altered data. We should always use the Cookie::get() method to retrieve any cookies we have created:
This will check the signature of the cookie to verify its authenticity, and then return the value as a string. The $username variable would now contain 'jdstraughan'. There is a second, optional parameter that will set a default value in the case that the key did not exist or does not pass validation. Deleting a cookie is at minimum a two-step process in PHP. First, we need to remove the cookie's name and value from the global $_COOKIE variable, and then we need to set the cookie's value to NULL, and expire the cookie by setting the expiration value to a time in the past. This can be a lot to remember, and is not fun to have to type every time we want to remove a cookie. Using the Cookie::delete() method turns this into a one step process:
This removes the cookie, expires it, and allows us to move on without having to go back and make sure we expired our cookie properly and deleted it from the $_COOKIE super variable.
The Encrypt (Encryption) Class While we are on the topic of security, it is worth looking into the encryption helper. It is considered a best practice to protect our users against the possible loss of data, and not have that data human readable outside of the application. A good example of this may be for the email addresses of our customers that are stored inside of a database. Recently, a very large web application had their database compromised, and all the user data was displayed for the entire world to see. This included email addresses in plain text, and passwords that had antiquated encryption techniques that were easy to decrypt. The hackers were able to show the email addresses, usernames, and passwords in human readable text for everyone to see.
[ 112 ]
Chapter 4
This scenario would have been easily avoided if the company in question had used proper encryption techniques when storing their user's data. Kohana's Encrypt class uses MCRYPT (http://mcrypt.sourceforge.net/), an encryption library that allows multiple encryption techniques to be easily applied to data. Without spending years of our lives studying cryptography, we can safely store and retrieve data in a few lines of code. Unlike most of the other helpers we have looked at, the Encrypt class relies on a configuration file to provide the information it needs to work.
Time for action – Creating configuration for Encryption Let's add an encryption configuration to the Egotist application. Although we will not be using it directly in the app just yet, once the database is added we will want to be able to easily add some data that needs to be encrypted.
1.
Create a new file in /application/config/ named encrypt.php. Open /application/config/encrypt.php for editing and add the following code: array( 'key' => 'narwhals-are-cool!42', 'cipher' => MCRYPT_RIJNDAEL_128, 'mode' => MCRYPT_MODE_NOFB, ), );
2.
Save the modified application/config/encrypt.php file.
What just happened? The Encrypt class has a constructor that sets up MCRYPT for us, and lets us know which cipher and mode to use, and what the unique key should be. Without this key, or password, the data cannot be decrypted. The key in the above example, 'narwhals-are-cool!42' is required to extract any data that is encrypted via the Encrypt class. If we change the key, all data that is in the database will not be accessible for decryption, so it is important to keep this code safe, especially in production environments. To see how this works, let's look at the methods we will use to create and extract encrypted data. [ 113 ]
Working with Helpers and Classes
Encoding, like all the other helper methods, is a simple one liner: encode('[email protected]'); ?>
Echoing out the contents of $email reveals an encrypted string: h7XFHONz2TjFtzA5BJFXuKusD9ogCE0rwu5eXPuatqSz5nTxA909F8IWaNekOaE=
This value will be different in your environment.
Because of the design pattern used in Encrypt, we cannot call the helper methods statically. By creating an instance of the object, we can then call the method through the normal means. We can shorten the code to one line if we are not going to be using the object later in the same method, like this: encode('super.secret.person@ example.com'); ?>
Because the Encrypt::instance() method returns an Encrypt object, we can chain methods for shorter code. The complimentary method to encode() is decode(), and as you can guess, it uses the key to decrypt the data, returning the original information: encode('super.secret.person@example. com'); $decrypted_email = $encrypt->decode($encrypted_email); echo $decrypted_email; ?>
This simple script will encode an email address, just like we saw above, and then decode it and print the contents in plain text. We can see how simple it is to encode and decode data in just a few lines of code. When we cover working with databases in Chapter 6, the encryption class will be used to store email addresses for our users. Whenever we are storing data that we think could pose a security risk, it is probably wise to encrypt it. [ 114 ]
Chapter 4
The Feed Class Almost every site on the web has an RSS or ATOM feed; it seems every client wants them, every site has them, and every developer has to make them. The Feed class gives us two methods to take care of RSS and ATOM feeds: one for creating a feed, and one for parsing them. Without having to create XML renderers and parsers, import third-party libraries, and waste hours of time checking all the nuances between the different feed versions and their requirements, we can just let Kohana do the hard work. To create a new RSS/ATOM feed, the Feed::create() method just needs the array of feed info (title, date created, pubDate, etc.) for the first parameter, an array of items for the second, and optionally, we can pass the format (rss2, atom, rss) as the third. Let's take a quick look at how easy it is to create a RSS feed: 'Test Feed' ); $items = array( array( 'title' => 'Test Feed Item One', 'summay' => 'This is a test feed item...', 'pubDate' => date('r', time()) ), array( 'title' => 'Test Feed Item Two', 'summay' => 'This is a test feed item...', 'pubDate' => date('r', time() - 2500) ) ); echo Feed::create($info, $items);
This will output the following RSS2 feed: Test Feed http://localhost/egotist/ KohanaPHPTest Feed Item One <summay>This is a test feed item... [ 115 ]
Working with Helpers and Classes Tue, 14 Dec 2010 18:39:55 -0600Test Feed Item Two <summay>This is a test feed item... Tue, 14 Dec 2010 17:58:15 -0600
Okay, we see more data here than what we passed into the method. First of all, the feed is RSS version 2.0, but we did not specify a format. This is because the format will default to RSS 2.0 if no format is provided. Once the database is in place for our case study site, we will be able to create the items array on the fly from our database. Here, we can see an example of an $info array with a title, and the $items array with some test news data. Once the arrays are created, with one line of code echoing the Feed::create() method, we will get the result. Next, we see the link and generator elements have been added for us, defaulting to the current URL and KohanaPHP as the generator. To replace these, we can simply add them to our $info array like this: $info = array( 'title' => 'Test Feed', 'link' => URL::site('some/link/location'), 'generator' => 'Egotist Beta' );
Notice anything? For the link value the URL helper is being used to create an absolute URL for the link to the feed location. Now, the beginning of the output feed should look like this: Test Feed http://localhost/egotist/some/link/location Egotist BetaTest Feed Item One <summay>This is a test feed item... Tue, 14 Dec 2010 18:47:36 -0600Test Feed Item Two [ 116 ]
Chapter 4 <summay>This is a test feed item... Tue, 14 Dec 2010 18:05:56 -0600
As of version 3.0.10 of Kohana, formats other than RSS2 are not supported, although the documentation does claim they are. RSS 1.0, 2.0, and ATOM 1.0 should all be implemented at some point, according to the documentation available at this time. Parsing a feed is even easier then creating one. We can now parse the feed we just created by adding the Feed::parse() method: 'Test Feed', 'link' => URL::site('some/link/location'), 'generator' => 'Egotist Beta' ); $items = array( array( 'title' => 'Test Feed Item One', 'summay' => 'This is a test feed item...', 'pubDate' => date('r', time()) ), array( 'title' => 'Test Feed Item Two', 'summay' => 'This is a test feed item...', 'pubDate' => date('r', time() - 2500) ) ); $feed = Feed::create($info, $items, 'atom'); $parsed = Feed::parse($feed); ?>
Now the feed is being created, and stored as a string in $feed. This could be any RSS feed from anywhere on the Web. The $parsed variable will now hold an associative array of items, looking like this: array(2) { [0]=> array(3) { ["title"]=> string(18) "Test Feed Item One" [ 117 ]
Working with Helpers and Classes ["summay"]=> string(27) "This is a test feed item..." ["pubDate"]=> string(31) "Tue, 14 Dec 2010 19:04:01 -0600" } [1]=> array(3) { ["title"]=> string(18) "Test Feed Item Two" ["summay"]=> string(27) "This is a test feed item..." ["pubDate"]=> string(31) "Tue, 14 Dec 2010 18:22:21 -0600" } }
The Feed::parse() method accepts an optional second parameter that will limit the number of items parsed and returned. If our feed had 50 items, for example, and we only wanted the first five, we could simply do this:
And we would get back an array consisting of no more than 5 items.
The Form Class Creating forms are probably not most web developer's favorite jobs. We have all created many different forms, validated them, rendered them, written the markup, and so on. Finding a programmatic means for rendering form elements is indeed a nice feature. Not only does this make more complex tasks like dynamically rendering forms easier, it makes just having to create forms in general easier. Let's take a look at a few of the helper methods, and then create a login form and message submission form for our case study site. To start any form, we need the opening form tag. The Form::open() method renders the tag and will by default use its current location for the action, and will default the method to 'post' and the accept-charset attribute to 'utf-8'.
will render: . Many of the standard form elements all have the same API for rendering their respective elements. Form::input(), button(), hidden(), password(), and submit() all basically do the same thing. They all render the form element after which they are named and they all accept three identical parameters. The first parameter for all three is the name of the element as a string. The second is the value for the element, and the third is an array of attributes that will be rendered like we saw above. In some of the helpers the second parameter is optional, but the third is optional in all of them. The