Tutorial: Building a blog
Initial Setup
You will need to install the following globally:
- git
- Composer
- docker and docker-compose
You should then run the following commands from the project root to get started
git clone https://github.com/RhubarbPHP/Bootstrap.WebApp.git
cd Bootstrap.WebApp
composer install #Download PHP Dependencies
docker-compose up #Start Virtual Machine
./custard.sh stem:seed-data #Seed some test blog articles.
Note that docker sets up the site to run on port 8080 which can sometimes conflict with the ports that other applications such as Skype use.
You will notice that a vendor directory has been created, this is where all of the third party dependencies are stored.
This is where you will find vendor/bin
which contains all of the executables.
Where is the home page?
If you open your browser at localhost:8080 you will find the "You're up and running!" start page.
Rhubarb uses a Model-View-Presenter pattern so, in src/Leaves/Index
you will find a Leaf (Presenter), a Model and
View: Index, IndexModel and Index View.
Open IndexView.php and change "You're up and running!" to the obligatory text " Hello, World!".
You will find that the home page text has changed.
In Index.php
you will notice that the Leaf class has been extended and that two methods:
getViewClass()
and createModel()
have been overridden. You must extend these
methods and return what they are expecting every time.
getViewClass()
expects a string to be returned of the View class that you have created.
createModel()
expects you to return a new LeafModel
class that you have extended.
Create an about us page
Create a new Directory in the src/Leaves
directory called AboutUs
. Inside this directory create 3 php files:
AboutUs.php
, AboutUsModel.php
, AboutUsView.php
AboutUs.php
Add a namespace to AboutUs.php
:
namespace Your\WebApp\Leaves\AboutUs;
(Quick explanation of namespaces: The src directory is Your\WebApp
, \Leaves\
is the Leaves directory and the
AboutUs
part of the namespace is the AboutUs
directory that we created. This is to allow the composer autoloader to
find our classes.)
Add a class called AboutUs
which extends \Rhubarb\Leaf\Leaves\Leaf
. For convenience you can add a use statement to
shorten this. Your file will now look like this.
<?php
namespace Your\WebApp\Leaves\AboutUs;
use Rhubarb\Leaf\Leaves\Leaf;
class AboutUs extends Leaf
{
}
Those of you using an IDE will notice that the class declaration is showing an error because we need to override two methods. Lets leave that until we create the other two classes so we can use them here.
In AboutUsModel.php
add the same namespace as before and your class declaration should be as follows:
class AboutUsModel extends \Rhubarb\Leaf\Leaves\LeafModel
You can simplify this with a use statement.
In AboutUs.php
override the createModel()
method and return a new AboutUsModel()
protected function createModel()
{
return new AboutUsModel();
}
In AboutUsView.php
add the namespace as before, and add the class declaration as follows:
(At this point I will assume that you will automatically switch to the use statements)
class AboutUsView extends \Rhubarb\Leaf\Views\View
In this class for now, override printViewContent()
so that we will have something to display
protected function printViewContent()
{
?>
<h1>About Us</h1>
<p>This is a blog site created using RhubarbPHP.</p>
<?php
}
You can also use print "Your content";
if you prefer, instead of opening and closing php.
In AboutUs.php
we can now override that last method: getViewClass()
. We need to return the class name of our view class.
protected function getViewClass()
{
return AboutUsView::class;
}
the last thing that we need to do is register our leaf AboutUs
in the main YourApplication class. You will find this
in the src/
directory: YourApplication.php
You will find a registerUrlHandlers()
method, which calls a method $this->addUrlHandlers();
This method accepts an array of url handlers. You will find the index one here already. Add an array to as the second
parameter to ClassMappedUrlHandler
and add a key value pair of "about-us/" => new classMappedUrlHandler(AboutUs::class)
If that seems daunting, just copy this for now.
protected function registerUrlHandlers()
{
parent::registerUrlHandlers();
$this->addUrlHandlers(
[
"/" => new ClassMappedUrlHandler(Index::class,
[
"about-us/" => new ClassMappedUrlHandler(\Your\WebApp\Leaves\AboutUs\AboutUs::class)
])
]
);
}
You will add all of your new Pages to this array. All page names should end with a /
http://localhost:8080/about-us/
is the url where we will now find the page.
If that seemed like a lot of work
Rhubarb uses custard commands to speed up the process of a lot of repetitive tasks.
Lets make a quick directory that we can remove in a moment.
Create a directory as follows:
src/Leaves/Delete
then cd
to the Delete
Directory
then
../../../vendor/bin/custard leaf:create-leaf
type in Delete
or whatever you want your leaf to be called then press enter and it will create a Leaf, Model and View
with a lot of helpful comments. You can delete this directory unless you want to keep it for reference.
Databases & tables
We need to require the Stem module to the project. In the project root enter the following command:
composer require rhubarbphp/module-stem
This will update your composer.json
and your composer.lock
files. In YourApplication.php
we will need to register
the Stem module. In getModules()
add new \Rhubarb\Stem\StemModule()
Create a Models
directory in src/
In here you will need to create a SolutionSchema
class and a Model class for each
table of your database.
Post Table
In the Models
directory create a new php file called Post.php
.
Give it a namespace: namespace Your\WebApp\Models;
it needs to contain a class as follows:
class Post extends \Rhubarb\Stem\Models\Model
In createSchema()
set it up to look like this:
protected function createSchema()
{
$schema = new \Rhubarb\Stem\Schema\ModelSchema("Post");
$schema->addColumn(
new \Rhubarb\Stem\Schema\Columns\AutoIncrementColumn("PostID"),
new \Rhubarb\Stem\Schema\Columns\StringColumn("Title", 50),
new \Rhubarb\Stem\Schema\Columns\LongStringColumn("Content")
);
$schema->labelColumnName = "Title";
return $schema;
}
All we are doing here is defining the columns in the table, and setting the labelColumnName which will be useful later.
Register the table in a SolutionSchema
In the Models directory create a new file called YourApplicationSolutionSchema.php
.
Give it the same namespace as before, and add a class declaration as follows:
class YourApplicationSolutionSchema extends \Rhubarb\Stem\Schema\SolutionSchema
You will need to override the constructor, call the parent and use the addModel()
method for our table as follows:
public function __construct($version = 0)
{
parent::__construct($version);
$this->addModel("Post", Post::class);
}
Register the SolutionSchema
First, have a look in the settings directory to see that vagrant has copied in a file called site.config.php with your database credentials. When you move to a live server you should create this file for yourself with your secure credentials. For our purposes in local development, this is sufficient.
In YourApplication.php
we need to register our SolutionSchema.
Override the initialise()
method, and call the parent.
In this method, you will need to include site.config.php
, register the SolutionSchema and set the default repository class name to the mysql
repository as follows:
protected function initialise()
{
parent::initialise();
if(file_exists(APPLICATION_ROOT_DIR . "/settings/site.config.php")){
include_once(APPLICATION_ROOT_DIR . "/settings/site.config.php");
}
\Rhubarb\Stem\Schema\SolutionSchema::registerSchema("YourApplicationSolutionSchema", \Your\WebApp\Models\YourApplicationSolutionSchema::class);
\Rhubarb\Stem\Repositories\Repository::setDefaultRepositoryClassName(\Rhubarb\Stem\Repositories\MySql\MySql::class);
}
Now ssh into vagrant using vagrant ssh
and do the following:
cd /vagrant
vendor/bin/custard stem:update-schemas
You will now have a database created with a Post table. While we are in here, we can also use the handy document command to give us PhpDoc comments to improve intellisense.
vendor/bin/custard stem:document-model
0
Select your schema 0
and press enter
You will find comments have been added to Post.php
, and a Post table has been created in your vagrant database.
If you are connecting to the database with HeidiSQL,Sequel Pro etc you will find the vagrant database in this example
running on localhost:3307
with username vagrant
and password vagrant
. We have chosen port 3307 so that a local
database will not be affected.
Details of ports can be found in the Vagrantfile
and database details in vagrant/provision.sh
should you need to
modify these to avoid ports colliding etc.
Display Posts from database
Using your database viewer of choice, manually add a few new posts in the database with a title and some lorem ipsum text for the content or whatever you want.
We will now display these posts on the HomePage.
Open IndexView.php
.
In the printViewContent()
method, lets create a variable of all of the posts and loop over them and print each of them
out.
protected function printViewContent()
{
parent::printViewContent();
?>
<h1>Rhubarb Blog</h1>
<?php
$posts = \Your\WebApp\Models\Post::all();
foreach ($posts as $post) {
?>
<h2><?= $post->Title ?></h2>
<p><?= $post->Content ?></p>
<?php
}
}
CRUD (Create Read Update Delete) Posts
first you will need to add a few dependencies:
composer require rhubarbphp/module-leaf-crud
composer require rhubarbphp/module-leaf-common-controls
composer require rhubarbphp/module-leaf-table
in src/Leaves
create a new directory called Posts
Create the following 4 files: PostsCollection.php
, PostsCollectionView.php
, PostsItem.php
and PostsItemView.php
They each need a namespace: namespace: Your\WebApp\Leaves\Posts
and they each need a class with the same as their file name
Table of Posts
PostsCollection
and PostItem
both ned to extend \Rhubarb\Leaf\Crud\Leaves\CrudLeaf
.
You will need to return the correct View class name for each of these.
PostCollectionView
and PostItemView
both need to extend \Rhubarb\Leaf\Crud\Leaves\CrudView
. You don't need to
override createModel this time until you need to.
In PostCollectionView
override the createSubleaves()
method as follows.
protected function createSubLeaves()
{
parent::createSubLeaves();
$this->registerSubLeaf(
$table = new \Rhubarb\Leaf\Table\Leaves\Table(\Your\WebApp\Models\Post::all(), 50, "PostsTable")
);
$table->columns =
[
"Title",
"Edit" => '<a href="/posts/{PostID}/">edit</a>'
];
}
The first thing that happens is we use registerSubLeaf()
this can take multiple leaves at a time separated by a comma.
We create a Table and set it's collection to Post::all()
, the page size to 50 and the presenter name to "PostsTable"
We assign our Table Leaf to $table
and then set the columns to an array of what we want to display.
The first column in the table we want to display is the Post Title
. This is case sensitive and must be exactly as
is in Post.php
.
To show the parsing feature, I have put the link to the edit page as the second column in the table. Note that {PostID}
is inside curly braces. This will be parsed into the PostID
for the current row that is being displayed.
We then need to print out this table in printViewContent()
as well as a link to the add page
protected function printViewContent()
{
parent::printViewContent();
print '<a href="/posts/add/">add</a><br>';
print $this->leaves["PostsTable"];
}
Create, Update & Delete Posts
In PostsItemView.php
again override createSubLeaves()
and printViewContent()
In non-CrudView
s, we would manually create a new TextBox
leaf etc however, Rhubarb can work out the best html element
to display our database content. All we need to do is pass in a string of each of the columns that we want to be able
to access to edit. If you want something more custom, you can still do this here but remember to set it's name to the
column name. The parent call to createSubLeaves() creates a Save
, Cancel
and Delete
button for us.
Copy this in to save time:
protected function createSubLeaves()
{
parent::createSubLeaves();
$this->registerSubLeaf(
"Title",
"Content"
);
}
protected function printViewContent()
{
print $this->leaves["Title"];
print "<br>";
print $this->leaves["Content"];
print "<br>";
print $this->leaves["Save"];
print $this->leaves["Cancel"];
print $this->leaves["Delete"];
}
Crud Url Handlers
Open YourApplication.php
and modify the registerUrlHandlers
method to look like this:
protected function registerUrlHandlers()
{
parent::registerUrlHandlers();
$this->addUrlHandlers(
[
"/" => new ClassMappedUrlHandler(Index::class,
[
"about-us/" => new ClassMappedUrlHandler(AboutUs::class),
"posts/" => new \Rhubarb\Leaf\Crud\UrlHandlers\CrudUrlHandler(\Your\WebApp\Models\Post::class, \Rhubarb\Crown\String\StringTools::getNamespaceFromClass(\Your\WebApp\Leaves\Posts\PostsCollectionView::class))
])
]
);
}
If you visit http://localhost:8080/posts/ you should now see a link to add a post, a table with two titles and the Posts that you created earlier. You can add and delete posts from here.
Static files (CSS & Images)
The design conscious among you may be wondering about the location of the <head>
tag to reference a css file etc,
also where to put a css file.
Static files go in the static
directory in the project root. In this directory you will find css/base.css
where you
can add custom styles. Lets add a font family to the existing body:
body {
margin: 0;
font-family: sans-serif;
}
Open src/Layouts/DefaultLayout.php
and add the reference to the css file in the <head>
like this
<link rel="stylesheet" href="/static/app.css">`
Images can be referenced from the same folder.