Disclaimer: I am learning this framework while writing this tutorial. So expect some noob errors.
Lets get started with the Cake framework. You can check out there website here.
Composer is required to run this application. So make sure you have this installed.
You can view the installation guidelines on their site here.
You need to have the php extension extension=php_intl.dll installed to continue this is very important.
To install php_intl.dll
I ran the following command. It might be different for you. Use the correct php versions
for you.
sudo apt-get install php7.0-intl
[cake_fig_install_command.png]
Once all the requirements are complete you can run the composer
command to make the app.
composer create-project --prefer-dist cakephp/app cake-tutorial
[Comment]
I experience errors when trying to install. For one I didnt have php_intl.dll
installed.
That means that when I ran the composer
command to create the app I also have to run the
composer install
command after I installed php_intl.dll
because the required packages were not installed.
Also in the config/app.default.php
file was changed to config/app.php
so that the application works.
[/Comment]
Be sure to create development domain name in your hosts file and add a special vhost configuration if you are using XAMPP.
Once the composer
command is successful you will have created your app.
[cake_fig_install_complete.png]
Lets create a standard hello world app. First we create a new Controller called DashboardController.php
. We create
this file in the src/Controller
folder. We add the code below.
<?php
namespace App\Controller;
class DashboardController extends AppController
{
public function home(){
echo "Hello World";
}
}
In our config/routes.php
file we add a new route to show our hello world app.
$routes->connect('/dashboard/home', ['controller' => 'Dashboard', 'action' => 'home']);
Thats it. You can see the hello world at the top in the image below. The rest of the page has an error but that's fine.
[cake_fig_hello_world.png]
Three folders we can note the config
folder the src
folder and the webroot
folder. These are the main directory
listings. In the src
folder we have more folders that are important.
/config - You config files
app.php
bootsrap.php
routes.php - your routing file
/src
controler/
AppController.php
DashboardController.php
Model/
Template/
View/
/webroot/
css/
font/
image/
js/
index.php
Our routing file is located in the config/routes.php
we can add routes to any function in our controller.
Lets try this out. Lets create a new function in our dashboard controller called help
.
public function help(){
$this->viewBuilder()->setLayout(false);
echo "a help page";
}
[Comment]
The $this->autoRender = false;
allows us to stop the auto rendering for controllers.
In our hello world example we add some errors because no layout was found this is because
the auto render looks for the layout automatically. We can disable this feature so we
wouldn't have this error.
If you want go back and try the hello world using the code
public function home(){
$this->autoRender = false;
echo "hello world";
}
[\Comment]
If we create a function in dashboard like
public function faq(){
$this->autoRender = false;
echo "this is the faq";
}
We can access this using http://dev.cakeapp.com/dashboard/faq
. Our base domain is dev.cakeapp.com
.
This means that we dont need to add a route in the routes.php
file. However we can add a route
$routes->connect('/dashboard/questions', ['controller' => 'Dashboard', 'action' => 'faq']);
Above we map the dashboard/questions
url to the faq
function in dashboard
.
Routing can do alot. You can learn more here.
We have already worked with controllers.Lets continue. Controllers are the C in MVC.
In our DashboardController.php
lets create another function called logs
;
public function logs( ){
$this->autoRender = false;
echo "this is the logs";
}
We can get data from our url
in our controller lets try that. Lets pass and id to our logs
function.
public function logs( ){
$this->autoRender = false;
echo "this is the logs" . $_GET['id'];
}
Now we navigate to http://dev.cakeapp.com/dashboard/logs?id=3
and we will see the proper message.
We can add more variables
public function logs( $id, $type ){
$this->autoRender = false;
echo "this is the logs" . $id . ' ' . $type;
}
And navigate to http://dev.cakeapp.com/dashboard/logs/3/errors
and we will see the proper message.
We can change this up. Add the code below
public function logs( $id ){
$this->autoRender = false;
echo "this is the logs" . $id;
}
Now we can navigate to http://dev.cakeapp.com/dashboard/logs/3
and we will see the proper message.
Lets create a private
function called users
. When we navigate to http://dev.cakeapp.com/dashboard/users
private function users(){
echo "users";
}
we get an error. We cant use private
functions as url
actions. The error page is shown below.
[cake_fig_private_users.png]
We can render views in our controllers by using the $this->render()
function.
Templates for our views are located in the src/Template/
directory.
Lets create a view for our users. In src/Template/Dashboard
directory create a file called users_view.ctp
and add the code below.
If the
Dashboard
folder doesnt exists create it.
<?php
echo "showing users";
Now make sure our DashboardController.php
has the correct function
public function users(){
$this->viewBuilder()->setLayout(false);
$this->render("users_view");
}
The $this->viewBuilder()->setLayout(false)
removes any pre set templates. You can remove it to see how
the page will render without it.
[cake_fig_no_layout.png]
Lets create a members view. Add the code below to your controller.
public function members(){
}
Now create the members.ctp
file in src/Template/Dashboard/members.ctp
. You can add the code below in the file
<?php
echo "showing the members template";
If you navigate to http://dev.cakeapp.com/dashboard/members
the contents of members.ctp
will be rendered.
This happens automatically. We don't have to use the $this->render()
function if our function is the same name
as the file to be rendered. Its suggested that we do though.
If you want to pass data from the controller to the view/template you can use the
$this->set()
function.
public function members(){
$this->set('members',['wynton','james']);
$this->render();
}
In our members.ctp
file we display the members
array using
<?php
foreach($members as $member){
echo $member .'<br>';
}
Lets try it a different way. We create a data
array and then use the set
function in our controller
public function members(){
$data = [
'name' => 'Wynton',
'age' => '23',
'dob' => 'neveruary'
];
$this->set($data);
$this->render();
}
Now in our members.ctp
we can add the following
<h1>Welcome to a members view</h1>
<h2>enjoy your stay</h2>
<p>The member is <?= h($name) ?> he is <?= h($age) ?>
years old and was born <?= h($dob) ?> </p>
So we can see the results. The h()
function is used to escape user content.
[cake_fig_data_to_view.png]
Lets go to the src/Layout
folder and copy the default.ctp
and name the copied file main.ctp
.
The main thing to do in main.ctp
is to add an h1
element as the page title.
<?= $this->Flash->render() ?>
<h1><?= $pageTitle;?></h1>
<div class="container clearfix">
<?= $this->fetch('content') ?>
</div>
Now in the DashboardController.php
we can set the layout to be the main.ctp
.
public function members(){
$this->viewBuilder()->setLayout('main');
$data = [
'name' => 'Wynton',
'age' => '23',
'dob' => 'neveruary',
];
$this->set('pageTitle', "Page Title");
$this->set($data);
$this->render();
}
Notice our content is being pulled into the main.ctp
layout using the
<?= $this->fetch('content') ?>
. In between this we have the rest of our layout.
[cake_fig_layout_sample.png]
Views are are classes and the view templates are the ctp
files that we created earlier.
Lets look at create a view template.
In the DashboardController.php
we create a new view
public function guide(){
$this->render();
}
Then we create the view template in src/Template/Dashboard/guide.ctp
.
<?php
echo "hello to the guide";
What else can we do in a view template. Lets change our layout
public function guide(){
$this->viewBuilder()->setLayout('main');
$this->render();
}
Now in our view we can change the pageTitle
<?php
$this->set('pageTitle', "This is the guide page");
$guideTitle = "Hello to the guide";
echo "<h1>" . $guideTitle . "</h1>";
echo "hello to the guide";
[cake_fig_view_pagetitle.png]
In order to use our cake model easily cake has some conventions that we should follow. You can check them out here.
Lets create a table to play with.
CREATE TABLE `countries` (
`country_id` int(11) NOT NULL AUTO_INCREMENT,
`country_name` varchar(65) DEFAULT NULL,
PRIMARY KEY (`country_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
You can insert data from a datasource or get the csv file from here.
Now lets update our database configuration in Cake
. Head to the config/app.php
file.
Find the database configuration part and add the correct information.
[cake_fig_database_config.png]
Now lets get into our models.
With Cake we can use different methods to access our database tables.
To use this class at the top of the controller we can add.
use Cake\ORM\TableRegistry;
Now we create a new route to display our countries in DashboardController.php
.
public function countries(){
$articles = TableRegistry::getTableLocator()->get('Countries');
$query = $articles->find();
$this->set('countries', $query);
$this->render();
}
In the countries.ctp
we add
<?php
foreach ($countries as $row) {
echo $row->country_name . '<br>';
}
[cake_fig_list_of_countries.png]
We can create an extension by create teh following code in src/Model/Table
.
We create a CountriesTable.php
class.
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CountriesTable extends Table
{
}
Now when we call $articles = TableRegistry::getTableLocator()->get('Countries')
we are getting and instance of
CountriesTable
. You can test this yourself by making modifications to the countries
function.
public function countries(){
$this->autoRender = false;
$articles = TableRegistry::getTableLocator()->get('Countries');
echo get_class($articles);
}
Learn more about tables here.
We can create an entity class by adding the following code in the src/Model/Entity
folder.
We will name our class Country.php
.
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Country extends Entity
{
}
Now in our CoutriesTable.php
we need to tell it what Entity
class to use.
We add the initialize
function and use the setEntityClass
function in it.
public function initialize(array $config)
{
$this->setEntityClass('App\Model\Entity\Country');
parent::initialize($config); // TODO: Change the autogenerated stub
}
Now we are using our entity class in our view (countries.ctp
). The $row
is an instance of Country.php
.
<?php
foreach ($countries as $row) {
echo get_class($row) . '<br>';
}
We can create virtual fields in our entity class. Below we add the _getCountrySpecialName
function
public function _getCountrySpecialName(){
return "The country name is: (" . $this->country_name . ")";
}
We can use this in our view like
<?php
foreach ($countries as $row) {
echo $row->get("countrySpecialName") . '<br>';
}
The output shows
[cake_fig_virutal_fields.png]
We can save a new entry to our countries
table by using the TableRegistry
class.
Lets create a create
function in our DashboardController.php
.
public function create(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$country = $countryTb->newEntity();
$country->country_name = "Wynton Island";
$countryTb->save($country);
echo var_dump($country);
}
We use the TableRegistry
to get and Table
instance of Countries
. We request a new entity
using $countryTb->newEntity()
.
We then create a new country $country->country_name
. And then we save it using
$countryTb->save($country);
In the database we can see our results.
[cake_fig_database_countries_new.png]
Lets create an update
function in our DashboardController.php
and use it to update an entity.
public function update(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$country = $countryTb->get(1000);
$country->country_name = "Wynton Updated Island";
$countryTb->save($country);
echo var_dump($country);
}
One of the important things to note in the above code is $countryTb->get(1000)
. We use
the get
function on the TableRegistry
class to get result sets by the primary key.
[cake_fig_database_countries_updated.png]
In the DashboardController.php
lets create a view
function and try to get results from
the database using different methods.
We can get data via the primary key
public function view(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$country = $countryTb->get(1);
echo $country->country_name;
}
Lets get all the countries with a limit.
public function view(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$countries = $countryTb->find("all")->limit(5);
foreach ($countries as $country){
echo $country->country_name . "<br>";
}
}
[cake_fig_all_countries.png]
Lets get all the countries that begin with a limit 100.
public function view(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$countries = $countryTb->find("all",[
'conditions' => 'Countries.country_name LIKE "a%"'
])->limit(100);
foreach ($countries as $country){
echo $country->country_name . "<br>";
}
}
lets get the country trinidad. We use $country = $countries->first()
to get the first result.
public function view(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$countries = $countryTb->find("all",[
'conditions' => 'Countries.country_name = "Trinidad ( T&T)"'
])->limit(100);
$country = $countries->first();
echo $country->country_name . "<br>";
}
Learn more here.
Lets create a form. First we create a new route in the DashboardController.php
public function form(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
$model = $countryTb->newEntity();
$this->set('model',$model);
$this->render();
}
In our view form.ctp
we can start building the form using the model we passed.
<div style="margin: 15px auto; max-width: 60%;">
<?php
echo $this->Form->create($model);
echo $this->Form->control('country_name', ['type' => 'text']);
echo $this->Form->button('Submit',['type'=>'submit']);
echo $this->Form->end();
?>
</div>
This will output the image below.
[cake_fig_form_view.png]
We can look at the generated html
that the form creates
[cake_fig_form_html.png]
When we press the form submit button we submit the form back to the original route
at /dashboard/form
. How do we handle this the code below show us
public function form(){
$this->autoRender = false;
$countryTb = TableRegistry::getTableLocator()->get('Countries');
if(isset($_POST['country_name'])){
$model = $countryTb->newEntity();
$model->country_name = $_POST['country_name'];
$countryTb->save($model);
echo "Country " . $model->country_name . " saved";
}else{
$model = $countryTb->newEntity();
$this->set('model',$model);
$this->render();
}
}
We check if the form is submitted using isset($_POST['country_name'])
function.
We then go on to save the new country $countryTb->save($model);
.
If not form is submitted we just render the form as shown in the else
section.
You can learn more about forms here.
We looked at many different part of working with Cake the PHP framework. Be sure to checkout their cookbook
to learn more. This tutorial used the Cake 3.8.5
version.