The original article was published on my personal website: Improving your PHP framework with Composer (1) – View Loading


See https://github.com/johnlui/My-First-Framework-based-on-Composer for sample code for this tutorial


review

After the last series of tutorials, “Building your own PHP framework step by step using Composer”, we have built a PHP framework withRouteMVC architectureandORMFundamental framework of functionalityMFFC。 Next, we will continue to improve the project.

Let’s use it from now on.Worst of allPlace – View Loading Start. We will encapsulate a view loading class to help us load the view and pass variables into the view. This class will only expose a few simple interfaces, let’s use in the controller, let’s write code while laughing. Really laugh, laugh. -D


text

Design

The task of loading a view class is actually simple:

  1. Find the view file according to the view name to support the folder
  2. Convenient and elegant transfer of variable values into the view

In this article, we will not do without introducing a template engine, only to load files and pass variables.

Basic preparation

We need to introduce view loaders, which formally opens the door to componentization, so we need to do some preparatory work.

1. Start process componentization

takepublic/index.phpThe code inside is separated from the bootstrap and created.MFFC/bootstrap.phpDocument:

<?php
use Illuminate\Database\Capsule\Manager as Capsule;

// Define BASE_PATH
define('BASE_PATH', __DIR__);

// Autoload automatic loading
require BASE_PATH.'/vendor/autoload.php';

// Eloquent ORM
$capsule = new Capsule;
$capsule->addConnection(require BASE_PATH.'/config/database.php');
$capsule->bootEloquent();

modifypublic/index.phpFor:

<?php

// Define PUBLIC_PATH
define('PUBLIC_PATH', __DIR__);

// starter
require PUBLIC_PATH.'/../bootstrap.php';

// Routing Configuration, Start Processing
require BASE_PATH.'/config/routes.php';

At this point, we have separated the entry file from the starter and defined two global constants.BASE_PATHandPUBLIC_PATH


Here we need to pay special attention to the fact that the step of “introducing a routing profile” is not simply introducing a configuration file, the last line of the routing file.Macaw::dispatch();actually isActually execute a function in a controllerWhere, all the preparation conditions should be completed before loading the routing file, such as the initialization of Eloquent, and the initialization of the Composer package that we will use later, etc.


2. Introducing error page prompt component

We chose FILP / whoops as our error prompt component package.

modifycomposer.json

"require": {
  "codingbean/macaw": "dev-master",
  "illuminate/database": "*",
  "filp/whoops": "*"
},

Functioncomposer updateAnd thenbootstrap.phpFinally, add:

// Whoops error prompt
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();

Refresh http://127.0.0.1:81 and you should still get this page:

Using Composer to Improve Your PHP Framework (1) - View Loading

Next we will add the routing configurationUnmatched termError page, modificationconfig/routes.php

<?php

use NoahBuscher\Macaw\Macaw;

Macaw::get('', '[email protected]');

Macaw::get('fuck', function() {
  Echo "Success! ";
});

Macaw::$error_callback = function() {
  Throw new Exception ("route no match 404 Not Found");
};

Macaw::dispatch();

Now visit a randomly entered URL, such as http://127.0.0.1:81/asd, and we will see the following screen:

Using Composer to Improve Your PHP Framework (1) - View Loading

Is there a very familiar feeling!?

Unfortunately, this error alert package is the one Laravel uses, so we’re cute.MFFCWhen the frame grew up, it becameLaravelThe way it looks. %% >%

Implementing Loader

After finishing the basic preparation, we formally started to manufacture the view loader.

View loader is a pluggable component. We should put all the pluggable components in one place. It is recommended in MFFC that we put all the pluggable components in one place.MFFC/servicesNext.

The base component library provided by the CI framework is calledhelpersLaravel usesilluminate/supportThe package provides some reusable system functions. In fact, “illuminate / support” is already dependent on our ORM package “illuminate / database”, and now it can be used directly in the MFFC framework. For the Chinese documentation of this package, see http://laravel-China.org/docs/helpers

We did not put view loaders at the core of the system as we did in the CI framework for two reasons:

  1. Namespace-based and auto-loading calls save more resources
  2. In the era of mobile Internet and big front-end becoming more and more intense, the back-end is becoming more and more API-based and json-based. Many times there is no view, and there is no need to increase fearless consumption.

Let’s start implementing the view loader.

Newly buildMFFC/servicesFolder, and modifycomposer.jsonAll classes under this folder are automatically grouped into the root namespace:

"autoload": {
  "classmap": [
    "app/controllers",
    "app/models",
    "services"
  ]
}

Newly buildservices/View.phpThe document reads as follows:

<?php
/**
* \View
*/
class View
{
  const VIEW_BASE_PATH = '/app/views/';

  public $view;
  public $data;

  public function __construct($view)
  {
    $this->view = $view;
  }

  public static function make($viewName = null)
  {
    if ( ! $viewName ) {
      Throw new Invalid ArgumentException ("View name cannot be empty! "";
    } else {

      $viewFilePath = self::getFilePath($viewName);
      if ( is_file($viewFilePath) ) {
        return new View($viewFilePath);
      } else {
        Throw new UnexpectedValueException ("View file does not exist! "";
      }
    }
  }

  public function with($key, $value = null)
  {
    $this->data[$key] = $value;
    return $this;
  }

  private static function getFilePath($viewName)
  {
    $filePath = str_replace('.', '/', $viewName);
    return BASE_PATH.self::VIEW_BASE_PATH.$filePath.'.php';
  }

  public function __call($method, $parameters)
  {
    if (starts_with($method, 'with'))
    {
      return $this->with(snake_case(substr($method, 4)), $parameters[0]);
    }

    Throw new BadMethodCallException ("Method [$method] does not exist! "."
  }
}

Functioncomposer dump-autoloadAfter that, we can call this class directly in the controller.

modifycontrollers/HomeController.php

<?php
/**
* \HomeController
*/
class HomeController extends BaseController
{

  public function home()
  {
    $this->view = View::make('home')->with('article',Article::first())
                                    ->withTitle('MFFC :-D')
                                    ->withFuckMe('OK!');
  }
}

modifycontrollers/BaseController.php

<?php
/**
* \BaseController
*/
class BaseController
{

  protected $view;

  public function __construct()
  {
  }

  public function __destruct()
  {
    $view = $this->view;
    if ( $view instanceof View ) {
      extract($view->data);
      require $view->view;
    }
  }
}

modifyapp/views/home.php

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title><?php echo $title ?></title>
</head>
<body>
  <div class="article">
    <h1><?php echo $article['title'] ?></h1>
    <div class="content">
      <?php echo $article['content'] ?>
    </div>
  </div>
  <ul class="fuckme">
    <li>Fuck Me !</li>
    <li>
      <?php echo $fuck_me ?>
    </li>
  </ul>
</body>
</html>

Refresh, you will see the following page:

Using Composer to Improve Your PHP Framework (1) - View Loading

At this point, the view loader implementation is complete.


Let me outline the basic ideas for designing view loaders:

  1. This view loader class mimics Laravel’s View class and implements a static methodmakeAccept the view name as a parameter to.As an interval between directories.
  2. The make static method checks whether the view name is empty, whether the view file exists, and gives the corresponding exception. That’s why we introduced exception handling packages.
  3. When the view name is valid and the file exists, an object of the View class is instantiated and returned.
  4. Usewith('key', $value)Or elegantwithKey($value)To insert variables to the View object to be invoked in the view.withFuckMe($value)The snake-like nomenclature will be transformed into$fuck_meFor view use.
  5. The final assembled View object is assigned toHomeControllerMembership variables$viewThis variable is fromBaseControllerIt can be inherited from China.
  6. Parent classBaseControllerThe destructive function in__destruct()Will be infunction home()Execute post-processing for this member variable:extractThe variables to be used in the view are shown.requireView file, send the final calculation results to the browser, the process ends.

Next step: Use Composer to improve your PHP framework (2) – Send mail