Why You Should Avoid Static Functions

Static functions are one of those "Laravel Encouraged" patterns that might seem handy at first, but will become a maintenance nightmare down the road.

Suppose we're building an E-Commerce site that allows you to buy and sell bulk spices online. In this app, theres a class in your application called QuantityUnits that passes us back a list of possible quantities (grams, ounces, pounds, etc). We access this list using the static function

QuantityUnits::getValidUnits();

We use QuantityUnits in a class that allows us to buy a product. Lets call it PurchaseProduct. PurchaseProduct is a catch all class that handles credit card processing etc and accepts a product to buy

class QuantityUnits
{
    public static function getValidUnits()
    {
        return [
            "grams",
            "ounces",
            "pounds",
        ];
    }
}
class PurchaseProduct
{
    public function purchase(Product $product, $quantity, $units, $credit_card_num)
    {
        // Make sure the units are valid before making the purchase
        if( is_buying_a_valid_unit_of_measure($units, QuantityUnits::getValidUnits()) {
            // Purchase spices
        }
    }
}

This will work fine at first, but consider the scenario where a year from now, it turns out that customers prefer buying spices from local vendors and your business just isn't working out. Instead, you decided to pivot your business to selling novelty t shirts instead. You already have the bones of an e-commerce site so it should be pretty easy to repurpose your code sell clothing.

Instead of ounces, grams, etc, t shirts are sold in singles, and crates of 100. Even though we went to great lengths to make the PurchaseProduct class reusable, Because we used that static class inside of the purchase method, we cannot reuse it to sell t shirts. Our purchasing code is intrinsicly tied to the idea of selling spices. Sure, we could add more units to the QuantityUnits class to handle these new types, but it leaves the possiblility that someone might try to buy "a gram of shirts".

Instead, what we can do is pass in an object in the constructor of the purchase product class that provides valid units for that particlular item

class PurchaseProduct
{
    public function __construct(QuantityUnits $validUnits)
    {
        $this->validUnits = $validUnits;
    }

    public function purchase( Product $product, $quantity, $units, $credit_card_num)
    {
        // Make sure the units are valid 
        if( is_buying_a_valid_unit_of_measure($units, $this->validUnits->getValidUnits()) {
            // Purchase anything!!!
        }
    }
}
class SpiceQuantityUnits
{
    public function getValidUnits()
    {
        return [
            "grams",
            "ounces",
            "pounds",
        ];
    }
}
class ShirtQuantityUnits
{
    public function getValidUnits()
    {
        return [
            "single",
            "crate",
        ];
    }
}

Now our purchase product class can be easily replaced with new business requirements.

Similar Blog Posts

A Simple Trait Validation Technique

I show off a common Trait issue and what I do to work around it

Converting Legacy PHP to Laravel

The strategy I used to rewrite an entire legacy PHP application in Laravel by myself

Some Helpful Vuex Tips

A couple of helpful tips for working with Vuex