Probably one of the most powerful characteristics of Phalcon is the
dependency injection (DI). If you have no idea about dependency injection, you should read at least the wiki page for this design pattern at http://en.wikipedia.org/wiki/Dependency_injection:
"Dependency injection is a software design pattern that implements inversion of control for resolving dependencies. An injection is the passing of a dependency (a service or software module) to a dependent object (a client). The service is made part of the client's state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
Dependency injection allows a program design to follow the dependency inversion principle."
The term "Dependency injection" was coined by Martin Fowler.
A real-life example of dependency injection might be the following situation: Suppose you go shopping. At the mall, you will need a bag to put your groceries, but you forgot to take one when you left your home. In this case, you will need to buy a bag. In development, buying this bag can be quite expensive. So, what if your door has a scanner that scans your body for a bag, and will not open unless you have one? This can be called dependency injection.
Phalcon uses the \Phalcon\DI
component, which is a component that implements the Inversion of Control pattern. This reduces the overall code complexity.
The framework itself or the developer can register services. Phalcon has many built-in components that are available in the DI container, such as the following ones:
- Request and response
- Logger
- Crypt
- Flash
- Router and configuration
- View
- Cache
- Session
Setting up a new component in the DI is as easy as the following code:
When you need to access the "mail" component, in a controller for example, you can simply call it:
If you need to create your own DI, Phalcon or the DiInterface
interface must be implemented to replace the one provided by Phalcon, or you must extend the current one.
These are just a few dummy examples so that you can have an idea about Phalcon's DI by the time we start our project. In the meanwhile, please take your time and read the official documentation that can be found at http://docs.phalconphp.com/en/latest/reference/di.html.
The request component is probably one of the most used components in any framework. It handles any HTTP request (such as GET, POST, or DELETE, among others) and also provides a few shortcuts for the $_SERVER
variable. Most of the time, we will use the request component in the controllers. The Phalcon documentation (http://docs.phalconphp.com/en/latest/reference/mvc.html) states the following:
"The controllers provide the "flow" between models and views. Controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation."
In Phalcon, all controllers should extend the \Phalcon\Mvc\Controller
component, and the name of the public methods that we want to access via HTTP GET should have the suffix Action
. For example:
Okay. So, how do we use the request component? Easy! Do you remember that we talked about built-in components in the DI section? The request component is one of them. All we need to do is get the DI. Here is an example of how to get and use the request component:
These are just a few common methods that are built into the request component. Let's continue with the next important component—Response.
So, what can this component do? Well, pretty much everything that is response or output related. Using it, we can set headers, do redirects, send cookies, set content, and much more. Here is a list of common methods from this component:
The redirect
method accepts three parameters: a location (string), if it is an external redirect (this is a Boolean type which is by default false
), and a status code (http status code range). The following lines of code is the redirect method:
Another useful method is the setHeader
method:
The preceding example sets a header named APIKEY
with the value as AWQ23XX258561
. Sending headers is a common approach when you develop APIs. You can send any type of headers and overwrite current headers using this method.
Content related methods: setContent()
and setJsonContent()
. Let's take for example the following code:
When you need to send any JSON content, you should set the header as application/json
using the built-in method in the response object:
Now that we know the basics about response/request components, we might find ourselves in a situation where we may need to log different things, such as errors. For this, we need to check the logger component.
In a production environment, we cannot afford to throw errors or blank pages at the client. We will avoid this and log the errors in a log file. You will read more about this in the next chapters. To sum it up, we will implement a custom logger to our DI, catch exceptions, and then log them. For example, perform the following set of steps:
- Set the custom logger in DI using the following code:
- Create a method that will throw an exception, catch it, and log it, as follows:
In the preceding example, we try to execute a nonexistent method, and our code will throw an exception that we catch. It will log it and then redirect the user to a friendly error page, error/500.html
. You will notice that our logger component calls a method named error
. There are other methods that are implemented, such as, debug
, info
, notice
, warning
, and so on.
The logger
component can be transactional. (Phalcon stores the logs temporarily in memory, and later on, it writes the data to the relevant adapter.) For example, consider the following code snippet:
Crypt is a very useful component if someone needs to encrypt data and decrypt it on your side. One situation where you might want to use the crypt component is to send data over the HTTP get
method or save sensitive information in your database.
This component has many built-in methods such as encrypt
, decrypt
, getAvailableChipers
, setKey
, getKey
, and so on. Here is an example of using the crypt component in the HTTP get
method.
First, we overwrite the DI, and then we pass a key to it in order to avoid setting it every time:
Of course, you are probably never going to use it this way. The preceding example just demonstrates the power of this component. You might have noticed that there is a new DI method called flash. We are going to talk about it next.
This component is used to send notifications to the client and inform him or her about the status of the component's actions. For example, we can send a successful message after a user has completed the registration on our website or submitted a contact form.
There are two kinds of flash messages—direct and session—and both are available in DI. The direct method outputs the message directly and cannot be loaded on a future request. On the contrary, the session method, stores the messages in a session, and they are automatically cleared after they are printed.
Here is a common usage of flash direct and flash session, assuming that you have a page called register, and you post the data on the same page:
In our view, we will render the messages using the getContent()
method or content()
in the template engine Volt (we'll cover this later in the chapter).
If we need to redirect our user to another page (let's call it registerSuccess
), then we need to use the flash session method; otherwise, the message will not appear.
The register
template will contain a form with method post
and action
pointing to the create
method. The create
method will look something like this:
In the preceding example, we set the messages in the session using the flashSession
method, and we redirect the user back to the register page. In order to render the messages in our view, we need to call the method flashSession()->output();
.
Tip
The recommended way is to forward the request with the help of dispatcher, not using redirects. If you use redirects, the user will lose all the data that he or she filled in the form.
The router component helps us to map friendly URLs to our controllers and actions.
By default, if the rewrite module is enabled in your web server, you will be able to access a controller named Post
and the read
action like this: http://www.learning-phalcon.localhost/post/read
. Our code can look like this:
However, sometimes, this code is not apt if you need to translate the URLs into multiple languages, or if you need to name the URLs in a different way to how they are defined in the code. Here is a usage example for the router component:
In the preceding example, we map all the categories to the controller post
and action findByCategorySlug
. The router component allows us to use regular expressions for our URLs. With preg_match
, this can be represented as follows
By accessing http://www.learning-phalcon.localhost/video
, the request will be forwarded to the findByCategorySlug
action from the post controller:
The getParam()
method has three parameters. The first one is the name that we are searching for, the second parameter is an array of filters that can be applied automatically, and the third parameter is the default value in case the requested name does not exist or is not set.
We will discuss models in the next chapter. This was just a simple example of how you can use the router.
The router also supports a precheck of the request
method. You may be used to check whether the method is POST, DELETE, PUT, or GET, like this:
While this is perfectly correct, it is not very friendly for our code. Phalcon's router has this capability by which you can add the right type of request that you are expecting, without the need to check this in your code:
This is the basic usage of the router. As always, please read the documentation in order to learn everything about this component.
This component can handle configuration files of various formats by using adapters. Phalcon has two built-in adapters for it, which are INI and Array. Using INI files is probably never a good idea. Therefore, I recommend you to make use of native arrays.
What kind of data can or needs to be stored in these files? Well, pretty much everything that will be needed globally in our application, such as database connection parameters. In the old days, we used $_GLOBALS
(a big security issue), or we used the define()
method, and then gradually we started using it globally.
Here is an example of a config
file, and how we can use it:
The config
object can be converted back to an array by using toArray()
method:
Another useful method for this object is the merge
method. If we have multiple configuration files, we can easily merge them into one object:
Now, the $config
object will have the same content as it did before.
Tip
There are two other adapters that are not implemented yet (YAML and JSON), but you can use them if you clone Phalcon's incubator repository (https://github.com/phalcon/incubator). This repository contains a collection of adapters/helpers that might be integrated in Phalcon in the near future.
This component is used to render our templates. By default, the templates have the .phtml
extension, and they contain HTML and PHP code. Here are some examples on how to use the view:
- First, we set up the view in the DI using the following code snippet:
- Now, we can use this service as follows:
- Next, we need to create a view template that must look like this:
Simple, isn't it? This component also supports hierarchical rendering. You can have a base layout, a general template for posts, and a template for a single post. Let's take, for example, the following directory structure:
Phalcon will first render app/views/index.phtml
. Then, when we request for detailAction()
from the post controller, it will render app/views/post/details.phtml
. The main layout can contain something similar to this code:
And, the details.phtml
template will have the following content:
This component also allows you to pick different templates to set a render level, disable or enable the view, and much more.
Phalcon has a built-in template engine named Volt. If you are familiar with PHP template engines such as Smarty or
Twig, you will want to use them for sure. Volt is almost identical to Twig, and you will find it very useful—it is inspired by
Jinja (http://jinja.pocoo.org/). You can even use your own template engine, or any other template engine that you can find there.
In order to enable the Volt template engine, we need to make a small modification to our view service, and we need to create a Volt service; here is how to do this:
By adding this modification and voltService
, we can now use this template engine. From the inheritance point of view, Volt acts a little bit differently. We first need to define a main layout with named blocks. Then, the rest of the templates should extend the main layout, and we need to put our content in the same blocks as the main layout. Before we look at some examples, I will tell you a little bit about Volt's syntax, the details are as follows.
- The syntax for outputting data or for echoing content:
- The syntax for defining blocks:
- The syntax to extend a template (this should be the first line in your template):
- The syntax to include a file:
- The syntax to include a file and pass variables:
Tip
Please note the missing extension. If you pass variables, you MUST omit the extension.
- The syntax for control structures (
for
, if
, else
): - The syntax for the loop context:
- The syntax for assignments:
The list is long. Additionally, you can use expressions, comparison operators, logic operators, filters, and so on. Let's write a simple template to see how it works:
This component provides object-oriented wrappers to access session data. To start the session, we need to add the service into the DI container:
The following is a code example for working with session:
If you check Phalcon's incubator, there are many available adapters, such as Redis, Database, Memcache, and Mongo. You can also implement your own adapter.
To improve the performance of some applications, you will need to cache data. For example, we can cache the query results for a post. Why? Imagine 1 million views or posts. Normally, you will query the database for it, but this will mean 1 million queries (you can multiply this by at least 3, if you are using it, and for ORM—this means 3 million queries at least). Why? When you query, the ORM will act like this:
- It'll check if the table exists, in the information schema:
- Then, it'll check whether it's executing a "Describe" of the table:
- Then, whether it's executing the actual query:
- If the
user
table has relations, the ORM will repeat each of the preceding steps for each relation.
To solve this problem, we will save the post object into our caching system.
Personally, I use Redis and Igbinary. Redis is probably the most powerful tool, since it stores the data in memory and, saves the data on disk for redundancy. This means that every time you request the data from cache, you will get it from memory. Igbinary (https://pecl.php.net/package/igbinary) is a replacement for the standard php serializer. Here is an example cache service:
The cache component has the following methods that are commonly used: