I’ve been using PRADO for a long time. It’s not a very popular PHP framework, but since I discovered it I have liked its features:
- Extensive documentation (you can check the list of demos).
- Component-oriented and event-driven architecture (with a lot of components).
- Its separation of markup and behavior.
- The extensive use of javascript and ajax to get amazing interactive pages.
Of course, it has its drawbacks:
- A steep learning curve.
- It may be a little heavy for the server.
But I think it’s worth the effort.
I think PRADO development team is doing a great job. Last version 3.3.0 (February 15, 2016) has lots of improvements. The most important one is a full rewritten of its javascript components, now most of them depend only on jQuery (previously, they depended on Prototype.js).
I’d like to show a little demo. I’ve chosen to build a multi-step form. I think it’s a good opportunity to show some of PRADO’s components and to get an overview of PRADO’s organization of pages. I hope you will like it.
The final product
You can see the final product here. You can see it’s a registration form. I have divided the form into three steps and the user can navigate through them. To simplify the code, this version doesn’t validate the fields nor save them into any database (I will save these steps for next posts).
The full code
The code of a PRADO page is split into two files: the .page and the .php files (both of them have the same name). Each one of them has its own responsibilities. Let’s see them:
Home.page file
First, the Home.page file. You can see it has a lot of markup. It looks like HTML … but it’s slightly different (don’t worry, I’ll explain it later).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
<!DOCTYPE html> <html> <com:THead> <meta charset="utf-8" /> <title>Registration form Template | PrepBootstrap</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="<%= $this->assetUrl . '/bootstrap/css/bootstrap.min.css'%>" /> <link rel="stylesheet" type="text/css" href="<%= $this->assetUrl . '/font-awesome/css/font-awesome.min.css'%>" /> <style type="text/css"> span.progress-step { position:absolute; width: 100%; text-align: center; background-color: transparent; } div.step-progress { margin-bottom:20px; } </style> </com:THead> <body> <com:TForm> <div class="container"> <div class="page-header"> <h1><a href="/">jmjg.es</a> - PRADO Multi Step Registration form</h1> </div> <div class="container"> <div class="row step-progress"> <div class="col-lg-12"> <com:TJuiProgressbar Id="StepsProgress" Options.Max="3" Options.Value="1"> <com:TActiveLabel Id="ProgressTitle" CssClass="progress-step" Text=""/> </com:TJuiProgressbar> </div> </div> <div class="row"> <div class="col-lg-6 col-lg-offset-3"> <h3><com:TActiveLabel Id="Title" Text=""/></h3> <com:TActiveMultiView Id="MultiView" ActiveViewIndex="0" OnActiveViewChanged="viewChanged"> <com:TView> <div class="form-group"> <com:TLabel ForControl="InputName" Text="Enter Name"/> <div class="input-group"> <com:TActiveTextBox CssClass="form-control" Id="InputName" Attributes.Placeholder="Enter Name" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <com:TLabel ForControl="InputEmailFirst" Text="Enter Email"/> <div class="input-group"> <com:TActiveTextBox CssClass="form-control" Id="InputEmailFirst" Attributes.Placeholder="Enter Email" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <com:TLabel ForControl="InputEmailSecond" Text="Confirm Email"/> <div class="input-group"> <com:TActiveTextBox CssClass="form-control" Id="InputEmailSecond" Attributes.Placeholder="Confirm Email" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <com:TLabel ForControl="InputMessage" Text="Enter Message"/> <div class="input-group"> <com:TActiveTextBox Id="InputMessage" CssClass="form-control" TextMode="MultiLine" Rows="5" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> </com:TView> <com:TView> <div class="form-group"> <com:TLabel ForControl="NewsletterFrecuency" Text="Which frecuency do you want to receive our Newsletter?"/> <div class="input-group"> <com:TActiveDropDownList Id="NewsletterFrecuency" CssClass="form-control"> <com:TListItem Value="" Text="Select a frecuency" Selected="true"/> <com:TListItem Value="1" Text="Daily"/> <com:TListItem Value="2" Text="Weekly"/> <com:TListItem Value="3" Text="Monthly"/> </com:TActiveDropDownList> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <label>Which are your areas of interest?</label> <div class="checkbox"> <label><com:TActiveCheckBox Id="PhpChecked"/> PHP</label> </diV> <div class="checkbox"> <label><com:TActiveCheckBox Id="javaChecked"/> Java</label> </div> <div class="checkbox"> <label><com:TActiveCheckBox Id="javascriptChecked"/> Javascript</label> </div> </com:TView> <com:TView> <div class="checkbox"> <label><com:TActiveCheckBox Id="NewsletterChecked"/> I agree to receive the Newsletter <span class="glyphicon glyphicon-asterisk"></label> </div> <div class="checkbox"> <label><com:TActiveCheckBox Id="CommercialChecked"/> I agree to receive interesting commercial information</label> </diV> </com:TView> </com:TActiveMultiView> <div class="well well-sm"><strong><span class="glyphicon glyphicon-asterisk"></span>Required Field</strong></div> <com:TActiveButton Id="Previous" Text="Previous" CssClass="btn btn-info pull-left" OnClick="previousClicked"/> <com:TActiveButton Id="Next" Text="Next" CssClass="btn btn-primary pull-right" OnClick="nextClicked"/> <com:TActiveButton Id="Submit" Text="Submit" CssClass="btn btn-success pull-right" OnClick="submitClicked"/> </div> </div> </div> </div> </com:TForm> </body> </html> |
I have used this template to build my form. Later, we will compare this markup with the original html template, to check the differences.
Home.php file
Now, let’s see the Home.page file. You can see the code is very short. How is it possible? Because PRADO’s component are very smart. And they save you a lot of code (I will also explain this code later):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?php class Home extends TPage { public $assetUrl; private $steps; public function __construct() { parent::__construct(); $this->assetUrl = Prado::getApplication()->getAssetManager()->getBaseUrl(); $this->steps = array( 'Introduce your personal details', 'Introduce your preferences', 'Accept the conditions', ); } public function onLoad($param) { parent::onLoad($param); $this->getClientScript()->registerPradoScript('jquery'); $this->getClientScript()->registerScriptFile('bootstrap', $this->assetUrl . '/bootstrap/js/bootstrap.min.js'); } public function previousClicked($sender, $param) { $activeView = $this->MultiView->getActiveViewIndex(); $this->MultiView->setActiveViewIndex($activeView - 1); } public function nextClicked($sender, $param) { $activeView = $this->MultiView->getActiveViewIndex(); $this->MultiView->setActiveViewIndex($activeView + 1); } public function viewChanged($sender, $param) { if ($this->MultiView->ActiveViewIndex == 0) { $this->Previous->setVisible(false); $this->Next->setVisible(true); $this->Submit->setVisible(false); } elseif ($this->MultiView->ActiveViewIndex == 2) { $this->Previous->setVisible(true); $this->Next->setVisible(false); $this->Submit->setVisible(true); } else { $this->Previous->setVisible(true); $this->Next->setVisible(true); $this->Submit->setVisible(false); } $stepNumber = $this->MultiView->ActiveViewIndex; $stepTitle = 'Step ' . ($stepNumber + 1) . ': ' . $this->steps[$stepNumber]; $this->ProgressTitle->setText($stepTitle); $this->StepsProgress->Options->Value = $stepNumber + 1; $this->Title->setText($stepTitle); } public function submitClicked($sender, $param) { // very soon } } |
Home.page in detail
When the user requested an URL, the PRADO framework will calculate the requested page and will load the associated .page. This .page file has all the necessary HTML markup (and some intelligence).
Let’s read together the full Home.page file in detail:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html> <com:THead> <meta charset="utf-8" /> <title>Registration form Template | PrepBootstrap</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="<%= $this->assetUrl . '/bootstrap/css/bootstrap.min.css'%>" /> <link rel="stylesheet" type="text/css" href="<%= $this->assetUrl . '/font-awesome/css/font-awesome.min.css'%>" /> <style type="text/css"> span.progress-step { position:absolute; width: 100%; text-align: center; background-color: transparent; } div.step-progress { margin-bottom:20px; } </style> </com:THead> |
As you can see, it begins like any HTML5 standard document. But I have replaced the <head> tag for a PRADO THead component (we know it’s a PRADO component because it has the namespace com:). This PRADO component will render the <head> tag.
Inside this component, there are some standard html tags (meta, title, link). But, wait! we can read href="<%= $this->assetUrl . '/bootstrap/css/bootstrap.min.css'%>". What does it mean? The <%= ... %> tag is a way to ask PRADO «please, evaluate the expression we are passing inside this tag and render it into the final page». In this case, we need to evaluate and render the expression $this->assetUrl . '/bootstrap/css/bootstrap.min.css'. The object $this, in this context, refers to the page object itself. We will see this property page->assetUrl later when we read the Home.php file. This expression will return where our public resources are hosted. I have chosen this way to configure where the additional public resources we need for our page (bootstrap and font-awesome files) are hosted.
|
1 2 3 4 5 6 7 |
<body> <com:TForm> <div class="container"> <div class="page-header"> <h1><a href="/">jmjg.es</a> - PRADO Multi Step Registration form</h1> </div> <div class="container"> |
It continues with a standard body tag, followed by a PRADO TForm component. It’s mandatory to have this component (and only one instance) inside every page. It will render a standard form tag. I also encourage you to avoid to write any other form tag.
|
1 2 3 4 5 6 7 |
<div class="row step-progress"> <div class="col-lg-12"> <com:TJuiProgressbar Id="StepsProgress" Options.Max="3" Options.Value="1"> <com:TActiveLabel Id="ProgressTitle" CssClass="progress-step" Text=""/> </com:TJuiProgressbar> </div> </div> |
Now, PRADO’s magic begins: we write a new TJuiProgressbar component. It will render a fully working jQuery-UI Progressbar. In this example, we assign its properties Id, Options.Max and Options.Value (you can read the component’s documentation here).
The Id property
Along this example, we will create a lot of PRADO components. We will assign the Id property to several of them. This property is associated to the component and it will give us a way to point to each specific component from our php code (so we can manipulate the components from the server side). Please, don’t confuse this property to the html id attribute (PRADO will generate an id attribute for every rendered html element but its value may not be the same to this Id property).
Some PRADO components may include inside them some other components. In this case, our TJuiProgressbar component includes a TActiveLabel component inside. This component will render a label element (o a span element, depending on how you configure it). Its initial Text property is empty (but we will change it from our php code). We have also assigned its CssClass property (to assign a css class to our rendered component). You can read about the TActiveLabel component here.
|
1 2 3 4 5 6 7 8 9 10 |
<div class="col-lg-6 col-lg-offset-3"> <h3><com:TActiveLabel Id="Title" Text=""/></h3> <com:TActiveMultiView Id="MultiView" ActiveViewIndex="0" OnActiveViewChanged="viewChanged"> <com:TView> </com:TView> <com:TView> </com:TView> <com:TView> </com:TView> </com:TActiveMultiView> |
Next, we have a new TactiveLabel (with Id «Title», remind this) and a new type of component: a TActiveMultiView component. This component has three TView components inside. The TActiveMultiView component is responsible to render only one of its TView children at a time (you can check its documentation here). We have configured to show first our first TView (ActiveViewIndex is «0»). We have also configured its OnActiveViewChanged property to «viewChanged» (we’ll come to this property later, when we study the Home.php file).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<com:TView> <div class="form-group"> <com:TLabel ForControl="InputName" Text="Enter Name"/> <div class="input-group"> <com:TActiveTextBox CssClass="form-control" Id="InputName" Attributes.Placeholder="Enter Name" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <com:TLabel ForControl="InputEmailFirst" Text="Enter Email"/> <div class="input-group"> <com:TActiveTextBox CssClass="form-control" Id="InputEmailFirst" Attributes.Placeholder="Enter Email" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <com:TLabel ForControl="InputEmailSecond" Text="Confirm Email"/> <div class="input-group"> <com:TActiveTextBox CssClass="form-control" Id="InputEmailSecond" Attributes.Placeholder="Confirm Email" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <com:TLabel ForControl="InputMessage" Text="Enter Message"/> <div class="input-group"> <com:TActiveTextBox Id="InputMessage" CssClass="form-control" TextMode="MultiLine" Rows="5" Attributes.Required=""/> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> </com:TView> |
The content of the three TView components is very similar: they have inside the necessary PRADO components to render the html fields for each one of our three steps. This first TView, for example, contains the fields «Name», «Email», «Confirm Email» and «Messsage». Let’s compare this markup with the original html markup:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<div class="form-group"> <label for="InputName">Enter Name</label> <div class="input-group"> <input type="text" class="form-control" name="InputName" id="InputName" placeholder="Enter Name" required> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <label for="InputEmail">Enter Email</label> <div class="input-group"> <input type="email" class="form-control" id="InputEmailFirst" name="InputEmail" placeholder="Enter Email" required> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <label for="InputEmail">Confirm Email</label> <div class="input-group"> <input type="email" class="form-control" id="InputEmailSecond" name="InputEmail" placeholder="Confirm Email" required> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> <div class="form-group"> <label for="InputMessage">Enter Message</label> <div class="input-group"> <textarea name="InputMessage" id="InputMessage" class="form-control" rows="5" required></textarea> <span class="input-group-addon"><span class="glyphicon glyphicon-asterisk"></span></span> </div> </div> |
You can see the markup is very similar, except we have replaced the html label, input and textarea elements for its equivalent PRADO components TLabel and TActiveTextBox:
TActiveLabel vs TLabel
On the first part of our page, we used a TActiveLabel component. Now, we are using a TLabel. What’s the difference? To understand its difference I have to explain a little about PRADO’s evolution.
To implement its communication between the components (TLabel, TTextbox, TButton, …) rendered to the browser and the server side, first versions of PRADO used standard POST requests. This mechanism has some limitations. Thankfully, as browsers and javascript evolved, PRADO added a new communication protocol based on AJAX calls. To keep backward compatibilty, PRADO developers added a new generation of AJAX enabled components (TActiveLabel, TActiveTextbox, TActiveButton, …).
You can still use the «classic» components as long you don’t need AJAX interactions with them (that is, they are rendered when the page is first loaded and you don’t need to modify them during your AJAX interactions).
We will use some PRADO components:
- The
TActiveTextboxcomponent will render aninputhtml element or atextareaelement (when itsTextModeproperty is set to «Multiline»). - The
TActiveDropdownListcomponent will render aselecthtml element. - The
TActiveCheckboxcomponent will render aninputhtml element of typecheck.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
</com:TActiveMultiView> <div class="well well-sm"><strong><span class="glyphicon glyphicon-asterisk"></span>Required Field</strong></div> <com:TActiveButton Id="Previous" Text="Previous" CssClass="btn btn-info pull-left" OnClick="previousClicked"/> <com:TActiveButton Id="Next" Text="Next" CssClass="btn btn-primary pull-right" OnClick="nextClicked"/> <com:TActiveButton Id="Submit" Text="Submit" CssClass="btn btn-success pull-right" OnClick="submitClicked"/> </div> </div> </div> </div> </com:TForm> </body> </html> |
After the TActiveMultiView component we have three TActiveButton components. Each one will render an input html element (of type submit). You can see each component has defined an OnClick property. We will see their purpose when we read our Home.php file.
Home.php in detail
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php class Home extends TPage { public $assetUrl; private $steps; public function __construct() { parent::__construct(); $this->assetUrl = Prado::getApplication()->getAssetManager()->getBaseUrl(); $this->steps = array( 'Introduce your personal details', 'Introduce your preferences', 'Accept the conditions', ); } |
We define a new Home class which extends the PRADO’s TPage class. This way, our class will have all the properties and methods we need to manage our page.
We also define two properties, one of them is public (assetUrl) and the other is private (steps). In our construct method, we configure both properties:
stepsis very straightforward: it’s an array of our three steps.assetUrlis a bit complex: we set it to the public url where PRADO publishes its assets (to do so, we ask it to theAssetManagercomponent which is inside theApplicationcomponent).
Components in PRADO
PRADO has lots of components. And each component has its own responsibilities. The only problem is that the programmer must be familiar with the catalog of components and which are their responsibilities.
In this example, the AssetManager component is responsible to publish resources needed for our application (you can read its documentation here). You can get this component through the Application component. It’s a singleton, with a lot of responsibilies (you can read its documentation here).
Do you remember our Home.page file, we use the expression $this->assetUrl . '/bootstrap/css/bootstrap.min.css'? Well, I hope that you can understand it now: we are accessing our page‘s assetUrl property from our Home.page file (because we know it’s the url where we have published our public resources). That’s the reason why I have declared the assetUrl property as public (so, the Home.page file can access it).
|
1 2 3 4 5 6 7 |
public function onLoad($param) { parent::onLoad($param); $this->getClientScript()->registerPradoScript('jquery'); $this->getClientScript()->registerScriptFile('bootstrap', $this->assetUrl . '/bootstrap/js/bootstrap.min.js'); } |
Next we define an onLoad method. This method is inherited from our TPage parent class. PRADO pages are loaded and rendered following predefined stages. onLoad is a hook to the page’s load stage. In sum, we’re asking PRADO: «when this page enters its Load stage, please execute this additional code». What additional code do we need to execute this time? We need to register the javascript libraries that we need for our application (so, they will be properly rendered when the page’s Render stage comes). To do so:
- We load the standard (it’s included with PRADO) jQuery library (we do so by the
clientScriptcomponent, using itsregisterPradoScriptmethod). - As bootstrap is not included in PRADO, we have copied its files inside the public assets folder and we register it as an additional library (using again the
clientScriptcomponent, but this time we’re using itsregisterScriptFilemethod).
|
1 2 3 4 5 6 7 8 9 10 11 |
public function previousClicked($sender, $param) { $activeView = $this->MultiView->getActiveViewIndex(); $this->MultiView->setActiveViewIndex($activeView - 1); } public function nextClicked($sender, $param) { $activeView = $this->MultiView->getActiveViewIndex(); $this->MultiView->setActiveViewIndex($activeView + 1); } |
In our Home.page file, when we defined the buttons with Id «Previous» and «Next», we configure their onClick properties. It’s now time to explain them. Whenever the user clicks on these buttons, the defined method will be called on the server side. On these methods, we will update the current view (changing the TActiveMultiView‘s ActiveViewIndex property).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public function viewChanged($sender, $param) { if ($this->MultiView->ActiveViewIndex == 0) { $this->Previous->setVisible(false); $this->Next->setVisible(true); $this->Submit->setVisible(false); } elseif ($this->MultiView->ActiveViewIndex == 2) { $this->Previous->setVisible(true); $this->Next->setVisible(false); $this->Submit->setVisible(true); } else { $this->Previous->setVisible(true); $this->Next->setVisible(true); $this->Submit->setVisible(false); } $stepNumber = $this->MultiView->ActiveViewIndex; $stepTitle = 'Step ' . ($stepNumber + 1) . ': ' . $this->steps[$stepNumber]; $this->ProgressTitle->setText($stepTitle); $this->StepsProgress->Options->Value = $stepNumber + 1; $this->Title->setText($stepTitle); } |
Do you remember our Home.page file, when we defined the TActiveMultiView component?. We declared a OnActiveViewChanged property with value «viewChanged». We were asking PRADO: «whenever our TActiveMultiView component changes the active view, please call the method viewChanged defined in our page». What do we do in this method?:
- Depending on the current view active (we can find what it is using the
ActiveViewIndexproperty) we enable / disable the appropiate buttons. Each button, is accesible from our Home.php file because we have declared theirIdproperty. - We update our
TJuiProgressBar‘sOptions.Valueproperty to show the current progress on our progress bar. - We also update the
TActiveLabelcomponents withIdProgressTitle and Title.
These two last pieces of code are a great example of PRADO’s magic: we can react to changes on the fronted side from the server side, changing the desired elements of the frontend. Whenever the user clicks a navigation button:
- On the server side, the associated method will change the current
ActiveViewIndex. - This change will trigger the
viewChangedmethod. - This method will update the presentation: hide / show the appropriate
TActiveButtoncomponents, update theTJuiProgressbarcomponent and update theTActiveLabelcomponents. - All these updates, will be rendered and sent back to the browser.
Conclusions?
I hope you have liked this small demo of PRADO’s features. Of course, there’s room for improvement. But my goal was to have a glance of PRADO using a real page as clear as possible and focusing on the main topics.
I have always liked the way PRADO organizes your code:
- html markup in the .page files, php code in the .php files.
- Excellent integration between frontend and backend.
- You can always encapsulate any piece of code into a component (if you want to reuse it or you think it’s too complex to program it directly inside your .page or .php files).
- Lots of components you can reuse.
The main problem may be that you need to study the documentation to tame all available components and their behaviors (it may take a lot of effort).
Next posts, I would like to finish this example:
- Including field validations.
- Saving the form to a database.
See you soon!!!







Excelente articulo sobre PRADO y animarte a seguir usándolo.
Concuerdo con usted sobre la versatilidad que tiene este Framework, como dices no es el mas popular pero si es uno de los mas flexibles y poderosos.