Skip to content

Posts › Packages

Packages

Laravel Visitor

LaravelVisitor is a Visitor Design Pattern implementation for Laravel. It allows to easily execute processing of collections of arbitrary elements, without requiring to use repeated conditionals, thus improving code abstraction.

Laravel Visitor

Github repository: https://github.com/robertogallea/Laravel-Visitor

1. Introduction

LaravelVisitor is a Visitor Design Pattern implementation for Laravel. It allows to easily execute processing of collections of arbitrary elements, without requiring to use repeated conditionals, thus improving code abstraction.

Without Visitor:

php
 1public function process() 
 2{
 3  $result = '';
 4  foreach ($this->elements as $element) {
 5    if ($element instanceof FooClass) {
 6      $result .= ((FooClass)$element)->getData();
 7    } elseif ($element instanceof BarClass) {
 8      $result .= ((BarClass)$element)->getData();
 9    } elseif ($element instanceof BazClass) {
10      $result .= ((BazClass)$element)->getData();
11    }
12  }
13}

With Visitor:

php
 1public function process() 
 2{
 3  $visitor = new MyVisitor([
 4    new FooClass(),
 5    new BarClass(),
 6    new BazClass(),
 7  ]);
 8  
 9  $visitor->execute();
10  
11  $result = $visitor->getResult();
12}

All of the complexity is hidden in the MyVisitor class, which must define methods for processing classes. In the previous example, MyVisitor would be implemented as:

php
 1class MyVisitor extends Visitor
 2{
 3  private $result;
 4  
 5  public function getResult()
 6  {
 7    return $this->result();
 8  }
 9  
10  public function visitFooClass(FooClass $fooClass) 
11  {
12    $this->result .= ... ;
13  }
14  
15  public function visitBarClass(BarClass $fooClass) 
16  {
17    $this->result .= ... ;
18  }
19  
20  public function visitBazClass(BazClass $fooClass) 
21  {
22    $this->result .= ... ;
23  }
24}

Additionally this enforces SRP principle, since Domain Objects don't have to implement representational methods, which are only responsibility of the Visitor classes implementation (especially if several are required).

2. Usage

For using the package, you need to define at least one Visitor and some Visitee classes.

2.1 Visitees implementation

The only requirement for Visitees is to use the Visitable trait, so you can make any class visitable.

2.2. Visitors implementation

A Visitor class must impelemnt the CanVisit interface and subclass the Visitor abstract class, by defining the getResult() method.

Additionally, for each defined Visitee's you have to implement a processing method of your choice. For example, if you have a Book Visitee, you must define the method:

php
 1public function visitBook(Book $book) {
 2  ...
 3}

2.3 Visitors generation

To generate Visitor, you can launch the following artisan commands:

php artisan make:visitor MyVisitor

which by default creates classes in the Visitors folder.

3. Example usage:

3.1 Visitee implementation

Magazine.php

php
 1use robertogallea\LaravelVisitor\Models\Visitable;
 2
 3class Magazine
 4{
 5    use Visitable;
 6
 7    private $title;
 8    private $month;
 9    private $year;
10
11    public function __construct($title, $month, $year)
12    {
13        $this->title = $title;
14        $this->month = $month;
15        $this->year = $year;
16    }
17
18    public function getTitle()
19    {
20        return $this->title;
21    }
22
23    public function getMonth()
24    {
25        return $this->month;
26    }
27
28    public function getYear()
29    {
30        return $this->year;
31    }
32
33}

3.2 Visitor implementation

XMLVisitor.php

php
 1use robertogallea\LaravelVisitor\Models\Visitor;
 2
 3class XMLVisitor extends Visitor
 4{
 5    private $xml = '';    
 6
 7    public function visitMagazine(Magazine $magazine)
 8    {
 9        $this->xml .= '<magazine title="' . $magazine->getTitle() . '" ' .
10            'issue="' . $magazine->getMonth() . ' ' . $magazine->getYear() . '"></magazine>' . "\n";
11    }
12
13    public function getResult()
14    {
15        return $this->xml;
16    }
17}

3.3 Client code

php
 1
 2  $xmlCatalog = new XMLVisitor([
 3    new Magazine('PHP programming', 'July', 2019)
 4    new Magazine('The art of woodworking', 'August', 2019)
 5  ]);
 6
 7  $xmlCatalog->process();
 8  
 9  echo($xmlCatalog->getResult());        

will produce the following output:

html
 1<magazine title="PHP programming" issue="July 2019"></magazine>
 2<magazine title="The art of woodworking" issue="August 2019"></magazine>