It is considered a best practice to make use of interfaces as a means of establishing the classification of a set of classes, and to guarantee the existence of certain methods. Traits and Interfaces often work together, and are an important aspect of implementation. Wherever you have a frequently used Interface that defines a method where the code does not change (such as a setter or getter), it is useful to also define a Trait that contains the actual code implementation.
ConnectionAwareInterface, first presented in Chapter 4, Working with PHP Object-Oriented Programming. This interface defines a setConnection() method that sets a $connection property. Two classes in the Application\Generic namespace, CountryList and CustomerList, contain redundant code, which matches the method defined in the interface.CountryList looks like before the change:class CountryList
{
protected $connection;
protected $key = 'iso3';
protected $value = 'name';
protected $table = 'iso_country_codes';
public function setConnection(Connection $connection)
{
$this->connection = $connection;
}
public function list()
{
$list = [];
$sql = sprintf('SELECT %s,%s FROM %s', $this->key,
$this->value, $this->table);
$stmt = $this->connection->pdo->query($sql);
while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) {
$list[$item[$this->key]] = $item[$this->value];
}
return $list;
}
}list() into a trait called ListTrait:trait ListTrait
{
public function list()
{
$list = [];
$sql = sprintf('SELECT %s,%s FROM %s',
$this->key, $this->value, $this->table);
$stmt = $this->connection->pdo->query($sql);
while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) {
$list[$item[$this->key]] = $item[$this->value];
}
return $list;
}
}ListTrait into a new class, CountryListUsingTrait, as shown next:class CountryListUsingTrait
{
use ListTrait;
protected $connection;
protected $key = 'iso3';
protected $value = 'name';
protected $table = 'iso_country_codes';
public function setConnection(Connection $connection)
{
$this->connection = $connection;
}
}Application\Database namespace. Here is the new trait:namespace Application\Database;
trait ConnectionTrait
{
protected $connection;
public function setConnection(Connection $connection)
{
$this->connection = $connection;
}
}Application\Database\ConnectionAwareInterface:namespace Application\Database;
use Application\Database\Connection;
interface ConnectionAwareInterface
{
public function setConnection(Connection $connection);
}CountryListUsingTrait class. Note that as the new trait is affected by its location in the namespace, we needed to add a use statement at the top of the class. You will also note that we implement ConnectionAwareInterface to identify the fact that this class requires the method defined in the trait. Notice that we are taking advantage of the new PHP 7 group use syntax:namespace Application\Generic;
use PDO;
use Application\Database\ {
Connection, ConnectionTrait, ConnectionAwareInterface
};
class CountryListUsingTrait implements ConnectionAwareInterface
{
use ListTrait;
use ConnectionTrait;
protected $key = 'iso3';
protected $value = 'name';
protected $table = 'iso_country_codes';
}First of all, make sure the classes developed in Chapter 4, Working with PHP Object-Oriented Programming, have been created. These include the Application\Generic\CountryList and Application\Generic\CustomerList classes discussed in Chapter 4, Working with PHP Object-Oriented Programming, in the recipe Using interfaces. Save each class in a new file in the Application\Generic folder as CountryListUsingTrait.php and CustomerListUsingTrait.php. Be sure to change the class names to match the new names of the files!
As discussed in step 3, remove the list() method from both CountryListUsingTrait.php and CustomerListUsingTrait.php. Add use ListTrait; in place of the method removed. Place the removed code into a separate file, in the same folder, called ListTrait.php.
You will also notice further duplication of code between the two list classes, in this case the setConnection() method. This calls for another trait!
Cut the setConnection() method out of both CountryListUsingTrait.php and CustomerListUsingTrait.php list classes, and place the removed code into a separate file called ConnectionTrait.php. As this trait is logically related to ConnectionAwareInterface and Connection, it makes sense to place the file in the Application\Database folder, and to specify its namespace accordingly.
Finally, define
Application\Database\ConnectionAwareInterface as discussed in step 6. Here is the final Application\Generic\CustomerListUsingTrait class after all changes:
<?php
namespace Application\Generic;
use PDO;
use Application\Database\Connection;
use Application\Database\ConnectionTrait;
use Application\Database\ConnectionAwareInterface;
class CustomerListUsingTrait implements ConnectionAwareInterface
{
use ListTrait;
use ConnectionTrait;
protected $key = 'id';
protected $value = 'name';
protected $table = 'customer';
}You can now copy the chap_04_oop_simple_interfaces_example.php file mentioned in Chapter 4, Working with PHP Object-Oriented Programming, to a new file called chap_13_trait_and_interface.php. Change the reference from CountryList to CountryListUsingTrait. Likewise, change the reference from CustomerList to CustomerListUsingTrait. Otherwise, the code can remain the same:
<?php
define('DB_CONFIG_FILE', '/../config/db.config.php');
require __DIR__ . '/../Application/Autoload/Loader.php';
Application\Autoload\Loader::init(__DIR__ . '/..');
$params = include __DIR__ . DB_CONFIG_FILE;
try {
$list = Application\Generic\ListFactory::factory(
new Application\Generic\CountryListUsingTrait(), $params);
echo 'Country List' . PHP_EOL;
foreach ($list->list() as $item) echo $item . ' ';
$list = Application\Generic\ListFactory::factory(
new Application\Generic\CustomerListUsingTrait(),
$params);
echo 'Customer List' . PHP_EOL;
foreach ($list->list() as $item) echo $item . ' ';
} catch (Throwable $e) {
echo $e->getMessage();
}The output will be exactly as described in the Using interfaces recipe of Chapter 4, Working with Object-Oriented Programming. You can see the country list portion of the output in the following screenshot:

The next image displays the customer list portion of the output:
