relo80
relo80

Reputation: 285

Differentiating MVC layers in Codeigniter4 (Libraries vs Helper vs Models)

I am ask myself what are the basic differences between:

What criteria will aid me in the decision to write methods/functions in a library, helper, or model as a I build my application?

The CI4 documentation won't give me a answer for this, so I am hoping for some fact-based guidance.

Upvotes: 0

Views: 944

Answers (2)

mickmackusa
mickmackusa

Reputation: 47894

I have been maintaining a large project in CI3 for a few years now (I was not the original developer) and can offer my logic-based decisions for structuring the project into your mentioned layers. I assume most of my advice will still hold true for CI4.

Separation of concerns and responsibilities:

  • Controller

    • This layer should not perform any data processing itself.
    • The goal is to keep these methods as short/skinny (fat controllers are a code smell).
    • The role of a controller is to call upon the appropriate layers to perform the activities required for the request.
    • This layer may display data when a view is not appropriate. For instance, printing a json-encoded payload to the client-side from an ajax request or API call is fine.
    • Think Remy from Ratatouille -- that rat isn't doing any cooking directly.
  • Model

    • Typically, this layer's only purpose is to access/manipulate database data.
    • Each method should only do 1 thing.
    • Data formatting should be kept to an absolute minimum.
    • Business logic should not be welcome here.
    • Model method should be unaware of the source of their data -- superglobal values should not be accessed directly.
    • All passed in data MUST come via the method signature for clarity and utility.
    • This layer should never display data; it should always return data (or throw an exception).
    • Think The Blaster from Thunderdome -- there's no independent thinking being done here.
  • Helper

    • This layer is merely a collection of functions.
    • Resist the urge to bake business logic into this layer.
    • Because these functions will need to specially call the CI instance to be able to communicate with other layers and your IDE may struggle to locate other layers to improve the UX, this should indicate the practice as inappropriate.
    • If you think you need to access a model or library method here, you're probably making a bad design decision.
    • Write your tedious data sanitation/validation/preparation/formatting tasks here to keep your code elsewhere lean and clean.
    • This layer should never display data.
    • Think of this layer as a set of cookie cutters. It doesn't matter if you are making food or using Play-Doh, simple input and simple output.
  • Library

    • This layers is the ideal place for writing business logic and generally should only be called by controllers.
    • You can make as many library classes as you need to define/group/separate all business logic.
    • Library logic may transcend different models and be called by different controllers as needed.
    • This layer should impose business logic on data retrieved from models, call helper functions to prepare data and return a payload to its calling layer.
    • This layer should never display data; it should always return data (or throw an exception).
  • View

    • This layer is fully about data presentation and never returns anything.
    • This layer AT MOST should be iterating and executing non-business-specific conditional logic (like locale-specific formatting, trivial sorting, hiding elements, etc). If business-logic creeps into this layer, the project will be harder to maintain.
    • There is no reason to try to directly access superglobals, session data, models, libraries in this layer; everything that is required should be explicitly passed in from the controller.
    • Think of this layer as the canvas on an easel; you only get your brushes and your pallette of paints -- use only the tools that you've been provided.

Upvotes: 0

parttimeturtle
parttimeturtle

Reputation: 1215

The documentation is pretty straight forward when it comes to Models, there's really no caveats there. A Model is a class that represents a single table in the database and it provides to you a wide variety of related functionality: built-in CRUD methods, the ability to save Entities, transparent use of Query Builder methods, etc.

In general, you would typically have one Model class per database table that you're working with. That being said, you do not necessarily need Models in order to work with a database; however if you expect to need the functionality a Model provides, it can be used to save you the work of doing it yourself.

The documentation is indeed far more opaque on the differences between Libraries and Helpers. As such, I've found the most objective measure of difference to be in how CodeIgniter itself utilizes them, and the distinction is rather subtle:

Libraries provide their functionality through methods that exist within the namespace of the class they're defined in.

Helpers provide their functionality through functions that exist within the namespace of the importing class.

(NB: In PHP, a method is simply the name for a function that's defined within a class)

In other words, Libraries are typically non-static classes that (like all non-static classes) must be 'constructed' before use. The methods defined within that class reside within the namespace of the class itself, not the class they're called from.

For example, to gain access to the current Session object, I retrieve an instance of the Session Library class: $session = session(); Using $session, I can then invoke methods provided by that Session class, like $session->destroy().

On the other hand, Helpers are typically just a collection of functions that are imported into the namespace of the importing class itself. They are called in the current context and their use is not predicated upon the use of an object.

For example, if I loaded the Array Helper (helper('array');), it would grant me access to a handful of functions that I could call directly from the current context (e.g. $result = array_deep_search(...)). I didn't have to create an object first, that function was imported directly into my current namespace.

This distinction could matter for a couple of reasons, the biggest of which is probably naming conflicts. If you have two Helpers, each with an identically-named function, you can't import both of those functions at the same time. On the other hand, you could have one hundred different classes with the destroy() method defined, because each of those method definitions resides within the namespace of the defining class itself and is invoked through an instance of that specific class.

You may notice that all of CI's Helper functions are prefixed with the name of the Helper itself, e.g. 'array' or 'form'; this is to prevent the aforementioned naming conflict.

This probably doesn't answer when you want to use one or the other, and in truth that really comes down to your opinion. In the end, it's all just namespacing. You can put things wherever you want, as long as the code that needs it knows where to look for it. I personally try to follow CI's lead on things, so I tend to follow their distinction between the two.

Upvotes: 1

Related Questions