Skip to content

Commit

Permalink
Update docs for release
Browse files Browse the repository at this point in the history
  • Loading branch information
MGatner committed Jan 9, 2022
1 parent b842cfa commit d89c746
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 86 deletions.
166 changes: 91 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Tatter\Handlers
Cross-module handler registration, for CodeIgniter 4
Cross-module handler management, for CodeIgniter 4

[![](https://github.com/tattersoftware/codeigniter4-handlers/workflows/PHPUnit/badge.svg)](https://github.com/tattersoftware/codeigniter4-handlers/actions/workflows/test.yml)
[![](https://github.com/tattersoftware/codeigniter4-handlers/workflows/PHPStan/badge.svg)](https://github.com/tattersoftware/codeigniter4-handlers/actions/workflows/analyze.yml)
Expand All @@ -14,50 +14,19 @@ Cross-module handler registration, for CodeIgniter 4

## Features

**Handlers** allows developers to define and discover supported classes for dynamic driver
types across all namespaces. An illustration may be more helpful...

### Example

*See also [Tatter\Thumbnails](https://github.com/tattersoftware/codeigniter4-thumbnails)*

You are creating an open source eCommerce site and you want to be able to display thumbnails
for anything in the shop. Some of the products will have simple photos, but others will be
digital content like 3D models, PDF text, or AI-generated data. Your `Thumbnails` library
handles converting a file type into an image, but what if someone else wants to support a
new file type? No problem! They simply create a new handler class in their module under
**src/Thumbnails/** and `Handlers` will detect it. Your example library might look like this:

```
use Tatter\Handlers\Handlers;
class Thumbnails
{
public function createForFile(string $file)
{
$extension = pathinfo($file, PATHINFO_EXTENSION);
// Initialize Tatter\Handlers
$handlers = new Handlers('Thumbnails');
// Get a handler for this file type
$class = $handlers->where(['extension' => $extension])->first();
$thumbnail = new $class($file);
echo $thumbnail->generate();
}
...
}
```
**Handlers** allows developers to define and discover classes of predetermined types across
all namespaces by a variety of attributes: "a database-free model for classes".

## Installation

Install easily via Composer to take advantage of CodeIgniter 4's autoloading capabilities
and always be up-to-date:
* `> composer require tatter/handlers`
```bash
composer require tatter/handlers
```

Or, install manually by downloading the source files and adding the directory to
`app/Config/Autoload.php`.
**app/Config/Autoload.php**.

## Configuration (optional)

Expand All @@ -71,66 +40,113 @@ in the comments. If no config file is found in **app/Config** the library will u
new folder in your project or module with an appropriate name for your implementation,
e.g. **app/Widgets/** or **src/Invoices**.

In order for them be discovered as handlers, your classes need to implement `HandlerInterface`.
For convenience, both a Trait and a base handler are provided as part of this module,
so these are all valid handlers:
### Interfaces

```
class MyClass extends \Tatter\Handlers\BaseHandler
{
...
In order for them be discovered as handlers, your classes need to implement the interface.
Supply the following static methods then implement `Tatter\Handlers\Interfaces\HandlerInterface`.
* `public static handlerId(): string`: Returns a unique slug identifier for this class
* `public static attributes(): array`: Returns an array of attributes about this handler

class MyClass implements \Tatter\Handlers\Interfaces\HandlerInterface
{
use \Tatter\Handlers\Traits\HandlerTrait;
...
If you would like to filter by a more specific version you may supply an extended version
of `HandlerInterface` via the method `BaseFactory::getInterface(): string`.

class MyClass implements \Tatter\Handlers\Interfaces\HandlerInterface
{
// Your own implementation
...
```
> Note: See **src/Interfaces/HandlerInterface.php** for more details.
Your classes will also need an `$attributes` array property to hold identifying information
for each handler. See **src/Interfaces\HandlerInterface.php** for more information.
### Factories

Once your classes are created, initialize the library with the relative path to your handlers:
Once your handler classes are set up you will need to create a Factory that provides the
lookup path and (optional) interface to identify the handlers. Create the new class and
and extend `BaseFactory`:
```php
<?php

$handlers = new \Tatter\Handlers\Handlers('Widgets');
namespace App\Factories;

You can then use the **Handlers** methods to locate all classes, or a subset of classes based
on their attributes:
use Tatter\Handlers\BaseFactory;

class WidgetFactory extends BaseFactory
{
/**
* Returns the search path.
*/
public function getPath(): string
{
return 'Widgets';
}
}
```

You can then use the `BaseFactory` methods to locate all handler classes, or a subset of
classes based on their attributes:
```php
$widgets = new WidgetFactory();

// Iterate through all discovered handlers
foreach ($handlers->findAll() as $class)
foreach ($widgets->findAll() as $class)
{
$widget = new $class($param1, $param2);
$widget->display();
$widget = new $class($param1, $param2);
$widget->display();
}

// ... or get a single handler by one of its attributes
$class = $handlers->where(['color' => 'red'])->first();
$class = $widgets->where(['color' => 'red'])->first();

// ... or by specifying its name
$class = $handlers->named('FancyHandler');
// ... or by specifying its handlerId
$class = $widgets->find('FancyHandler');
(new $class)->display();
```

If you want your Factory to search for a more specific interface then add the class string
as a return from your Factory's `getInterface()` method:
```php
class WidgetFactory extends BaseFactory
{
/**
* Returns the interface required for handlers to match.
*
* @return class-string<HandlerInterface>
*/
public function getInterface(): string
{
return 'App\Interfaces\WidgetInterface';
}
...
```

## Caching

**Handlers** scans through all namespaces to discover relevant classes. This distributed
filesystem read can be costly in large projects, so **Handlers** will cache the results
for an amount of time set in the config file (default: one day). Should you need to clear
this cache (for example, when adding a new module with additional handlers) you can use
the "reset" command:
for an amount of time set in the config file (default: one day). You can disable Caching
using the config file by setting `$cacheDuration` to `null`.

Often it is a good idea to pre-cache handlers so the filesystem search does not happen on
an actual page load. This library includes `FactoryFactory`, a "Factory to discover other
Factories". if you would like your Factories to be discoverable by `FactoryFactory` and
thus their handlers enabled for auto-caching then place your Factory classes in the
**Factories** subfolder and have them fulfill the `HandlerInterface` methods just like any
other handlers.

### Commands

To assist with `FactoryFactory`'s discovery of your factories and their handlers this
library includes two commands that will pre-cache all handler classes and clear the cached
values respectively:
```bash
# Discovers and caches all compatible factories and their handlers
php spark handlers:cache

# Clears all cached factories and handlers
php spark handlers:clear
```

php spark handlers:reset
Set your cron job to run `spark handlers:cache` on some interval smaller than the Config
`$cacheDuration` to ensure your handlers are always at hand.

Likewise, if you would like to pre-cache handler discovery to improve performance then
you can use the "list" command to discover and cache results:
## Examples

php spark handlers:list
Here are some other libraries that implement their own Factory class with a set of handlers.
Browse their code to get an idea of how you might use `Handlers` for your own projects.

Both of these commands rely on preset paths in the config file array `$autoDiscover`, so be
sure to set that before using them.
* [Tatter\Thumbnails](https://github.com/tattersoftware/codeigniter4-thumbnails): Modular thumbnail generation, for CodeIgniter 4
* [Tatter\Exports](https://github.com/tattersoftware/codeigniter4-exports): Modular file exports, for CodeIgniter 4
7 changes: 4 additions & 3 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@

Version 3 focuses on simplifying the code and making each class align more logically with what it does.

* `Handlers` has been refactored into `BaseManager`; read more below
* `Handlers` has been refactored into `BaseFactory`; read more below
* Related, the following have been removed: Handlers service, helper file, and command files
* The `$attributes` property and accessor methods have been replaced by a single static method: `attributes()`
* Identification of handlers is now handled via the static method `handlerId()` instead of the "name" or "uid" attributes, or the class itself
* THe "auto-discovery" feature is removed; read the docs on creating discovery-compatible factories instead

### `BaseManager`
### `BaseFactory`

`Handlers` is no longer a library with service and helper. The core of this library is now
centered around an abstract class `BaseManager` with the same discovery and lookup methods
centered around an abstract class `BaseFactory` with the same discovery and lookup methods
that were previously on `Handlers`. Other libraries needing handler discovery should extend
this class and provide the required `getPath(): string` method.
11 changes: 3 additions & 8 deletions examples/Handlers.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
*
*/

class Handlers extends \Tatter\Handlers\Config\Handlers
use Tatter\Handlers\Config\Handlers as BaseHandlers;

class Handlers extends BaseHandlers
{
/**
* Classes to ignore across all handlers.
Expand All @@ -21,13 +23,6 @@ class Handlers extends \Tatter\Handlers\Config\Handlers
*/
public $ignoredClasses = [];

/**
* Paths to check during automatic discovery.
*
* @var array<string>
*/
public $autoDiscover = [];

/**
* Number of seconds to cache discovered handlers.
* Null disables caching
Expand Down

0 comments on commit d89c746

Please sign in to comment.