This is the final stage of our development. Now that we have written all the working code, we must make it reusable but most importantly, maintainable. This chapter will help you to reuse code by means of widgets and other components. We will see some practical examples on how to use them. Then, we will deal with documentation, an important aspect of app development that allows everyone to quickly learn how a project is structured and built.
For the documentation, we are going to use the two most important tools provided by the framework in order to build API and guide references, making a real-life example. We will cover the following topics:
A widget is a reusable client-side code (containing JavaScript, CSS, and HTML) with minimal logic wrapped in a yiiaseWidget
object that we can easily insert and apply in any view.
Building a widget requires you to extend two methods of yiiaseWidget
:
init()
method initializes the objectrun()
method executes the objectIn order to instance a widget, it is enough to call the static widget()
method that accepts just one parameter or better still an array containing values for its public properties.
The following is an example:
MyWidget::widget(['prop1' => 'value of prop1', …])
This returns a string containing widget output, passing its value value of prop1
for its prop1
public properties.
If we need to insert an extra code in a widget's execution (for example, in the ActiveForm widget), we have a more complex way of instantiating the widget, using the begin()
and end()
methods.
The first method, begin()
, accepts a function parameter with a configuration array to pass to the widget, and it will return the widget object.
When the second method, end()
, is called, the code between these two methods will be displayed and simultaneously, the end()
method directly echoes the output of the widget run()
method:
$widget = MyWidget::begin(['prop1' => 'value of prop1', …]); .. .. I can use $widget object here .. .. MyWidget::end();
As for any other views, in the run()
method, we can refer to a view file, through the render()
method, in order to display the widget output.
For example, a widget could be a real-time date/time clock. For this purpose, we will build a clock based on a block containing the date/time string updated by the JavaScript code. We can pass to widget construct time some values concerning for example, the color of the border box.
To make an instance, let's start with the basic template app (but this is obviously also valid for the advanced template app). Create a new folder (if it does not exist) named components
in the root of the project at the same level of controllers
, models
, views
, and so on, which will contain all the widgets we want to build.
Then, in this folder, we will create a new file named ClockWidget.php
with the complete path basic/components/ClockWidget.php
:
<?php namespace appcomponents; use yiiaseWidget; class ClockWidget extends Widget { public function init() { yiiwebJqueryAsset::register($this->getView()); } public function run() { return $this->render('clock'); } }
In the init()
method, we have also made references to the jQuery asset to request the framework to load the jQuery plugin, since we need it in the view file.
In the run()
method, we have rendered the clock
view, whose content will be discussed in next rows.
So, create a new folder at basic/components/views
and, within it, a new file named clock.php
with the following code:
<?php $this->registerJs( <<< EOT_JS function ClockWidget_refresh_datetime() { var dateTimeString = new Date().toString(); $('#ClockWidget_realtime_clock').html(dateTimeString); } setInterval(ClockWidget_refresh_datetime,1000); ClockWidget_refresh_datetime(); EOT_JS ); ?> <div style="border:1px solid black;padding:5px;width:200px;text-align:center"> <span id="ClockWidget_realtime_clock"></span> </div>
This code simply displays a box with a string containing real-time values of the current date and time, updated every second.
Finally, we can use our widget in any view using this code:
<?= appcomponentsClockWidget::widget(); ?>
In this example, we will create a widget that consists of a carousel with some rooms (we can choose which one to display by passing them to the widget with the public property). Again, we will use a basic template application; however, everything is equally applicable to the advanced template apps.
For this example, we will create a new controller to use its view as a widget container.
So, let's create this new controller named TestCarouselController
at basic/controller/TestCarouselController.php
. From here, we will pass the models
property, consisting of a list of maximum three rooms:
<?php namespace appcontrollers; use yiiwebController; use appmodelsRoom; class TestCarouselController extends Controller { public function actionIndex() { $models = Room::find()->limit(3)->all(); return $this->render('index', ['models' => $models]); } }
Next, we will create the view at basic/views/test-carousel/index.php
with the widget output as follows:
This is a carousel widget with some rooms: <?= appcomponentsCarouselWidgetCarouselWidget::widget(['models' => $models, 'options' => ['style' => 'border:1px solid black;text-align:center;padding:5px;']]); ?>
This builds the widget filling and its public properties models
and options
.
Now it is time to create our widget. To isolate the widget from another code as much as possible, we create a specific widget folder at the basic/components
folder, under a subfolder named CarouselWidget
inside of which we will create the widget file named CarouselWidget.php
.
This widget includes a public property, models
that contains the room's model that has been passed from the container view. It is necessary to pass these models to the Carousel widget at yiiootstrapCarousel
as an array of this kind:
items => [ ['content' => '...', 'caption' => '...'], ['content' => '...', 'caption' => '...'], ['content' => '...', 'caption' => '...'], ... ];
In this way, in the init()
method, we will create an internal representation of the models according to the Bootstrap Yii2 widget expectation.
Finally, in the run()
method, we will output the view now in the views folder at basic/components/CarouselWidget/views
. This is the widget content; remember that it is stored in CarouselWidget.php
at basic/components/CarouselWidget
:
<?php namespace appcomponentsCarouselWidget; use yiiaseWidget; class CarouselWidget extends Widget { public $carouselId = 'carouselWidget_0'; public $options = []; public $models = []; private $carouselItemsContent; public function init() { // It is not necessary because yii bootstrap Carousel widget will load it automatically // yiijuiJuiAsset::register($this->getView()); $this->carouselItemsContent = []; foreach($this->models as $model) { $caption = sprintf('<h1>Room #%d</h1>', $model->id); $content = sprintf('This is room #%d at floor %d with %0.2f€ price per day', $model->id, $model->floor, $model->price_per_day); $itemContent = ['content' => $content, 'caption' => $caption]; $this->carouselItemsContent[] = $itemContent; } } public function run() { return $this->render('carousel', ['carouselItemsContent' => $this->carouselItemsContent]); } }
The widget view, called in the run()
method, will be stored in the carousel.php
file at basic/components/CarouselWidget/views
:
<?php $styleOption = isset($this->context->options['style'])?$this->context->options['style']:''; ?> <div id="<?php echo $this->context->id ?>" style="<?php echo $styleOption ?>"> <?php echo yiiootstrapCarousel::widget([ 'id' => $this->context->carouselId, 'items' => $carouselItemsContent ]); ?> </div>
Browsing to http://hostname/basic/web/test-carousel/index
, we will see the carousel widget (only text, but we can also insert some images within).
13.59.9.236