Factory Methods

php laravel

There was a time that I was updating the printed label creation process in the huge monolith.

Label size varied based on the item it was attached to. The size constricted the label printer choices, and each label had its own view file governing the display. If I remember, two different labels had vastly different view files that created the same output.

Many items need a printed label with either a QR-Code or Code-39 Bar code. Each location in the warehouse had a specific human-readable location, based on its row, shelf, and bay, and it needed a bar code for the scanner. All that data was placed on a printed label and attached to the shelf.

A case of product arrives at the warehouse. That case needs a printed label on a specific label size so it can be scanned for inventory or found at a later date. At some point in the fulfillment process, the case will be opened and moved to a different location in the warehouse. That opened case needed a new label, indicating that the case did not include exactly 72 identical items. When the product sells, a product will be pulled from the opened case, and that product needs a new label, indicating the order that it will fulfill. There were other printed labels in the system, but this just shows some cases for the project.

Before I was updating the label creation, each developer had to duplicate the process of gathering the objects and sending each one through a PDF printing system. Sounds simple, but there were multiple duplicated steps before creating the PDF, pulling a list of printers, and sending the PDF to the print queue. There was nothing preventing the user from sending a 4x6 label to a 2x1 label printer.

I was able to simplify the creation of the labels by creating an Interface for the label actions. The first thing was to create a Labelable interface to govern which objects could be converted to a label. Questions were raised as to whether an empty interface should be used, but using an empty interface allows type checking. Using a static property would add some complexity to the factory method just to check that the

interface Labelable {}

class ProductCase implements Labelable {...}

Then we had a factory method. We weren’t using PHP 8 at the time, so the match expression didn’t yet exist. The factory method takes a Labelable and returns a child of the LabelFactory interface.

The GOF book might suggest a method named createLabels(), Laravel might suggest using the handle() method. I prefer execute(). By seeing that a method is execute(), I know that we are running a Userland method, one that I created and can be modified. When I see handle(), I know that the method is defined by Laravel and probably shouldn’t be modified.

class LabelFactory {
        // Labelable class => child of LabelFactory::class
        private const array LABEL_MAP = [
        \App\Products\ShelfLocation::class => \App\Labels\ShelfLabel::class,
        \App\Products\ProductCase::class => \App\Labels\ProductCaseLabel::class,
        \App\Products\OpenProductCase::class => \App\Labels\OpenProductCaseLabel::class,
        \App\Products\Product::class => \App\Labels\ProductLabel::class,
    ];
    public static function execute(Labelable $labelable): LabelFactory {
        return new self::LABEL_MAP(get_class($labelable));
    }
}

No matter which object needs to print labels, we are returned a concrete instance of the LabelFactory class. I don’t remember the specifics anymore, but the code could be vastly simplified using the power of abstraction.

... // $product is some fulfillment product.
$labelFactory = LabelFactory::execute($product);
$printers = $labelFactory->validPrinters($warehouseLocation);
// somehow the user chooses a printer from the list of printers
PrinterQueue::add($labelFactory->createPdf(), $chosenPrinter);

Weather in Charlotte, NC