0% found this document useful (0 votes)
39 views

Codeigniter4foundations Sample

Uploaded by

purusottam007
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
39 views

Codeigniter4foundations Sample

Uploaded by

purusottam007
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 25

CodeIgniter 4 Foundations

Lonnie Ezell
This book is for sale at http://leanpub.com/codeigniter4foundations

This version was published on 2021-11-16

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

© 2016 - 2021 Lonnie Ezell


Also By Lonnie Ezell
Practical CodeIgniter 3
Contents

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Primer: Model-View-Controller (MVC) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3


What is MVC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Primary Class Purposes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Other Common Class Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

Primer: Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
The Simple Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Going Deeper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Primer: Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11


What is it? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
How to do it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
How to use it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Why Controllers are Exempt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Primer: Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
What Are They? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
How to Use Them . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
When to Use Them . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Creating Your Own . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Extending the Core Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

Project 1 - Brochure Site Take 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19


Routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Displaying Our Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Introduction

The original CodeIgniter logo

The first beta of CodeIgniter was released to the world on February 25, 2006 by pMachine. It was
described on its website as “an Open Source Web Application Framework that makes writing kick-
ass PHP programs simple as apple pie”. Inspired by Ruby on Rails, it was built by Rick Ellis as the
foundation for ExpressionEngine, a CMS that Rick and Paul Burdick had built for their own client
work. Well, built for is probably the wrong description. It was ripped out of the CMS and cleaned
up so it could be released on its own.
It rapidly built a large following of PHP developers that loved its speed, flexibility, and simplicity.
It came with a collection of 20 different libraries to help you get things done, and not spend time
re-building solutions. For many years it was one of–if not the–most widely used PHP framework.
Times have changed a lot since then, though. PHP itself, has grown up a lot, adding many
sophisticated features previously reserved for “real” languages, like Java. It’s gotten more powerful,
faster, and better able to create well-designed applications. CodeIgniter, however, did not keep up
with the technological advances in the language.
While I don’t know the specific reasons, I imagine that EllisLabs, the company that pMachine
morphed into and continued building ExpressionEngine until recently, didn’t think it was feasible
to rewrite the core of their primary business every time a new feature was added to PHP. Since the
framework grew up in parallel to ExpressionEngine, and was largely kept in sync with the needs
of ExpressionEngine developers, new features were typically few and far between. In time, the gap
between the common practices used in the wider PHP community, and the programming styles used
within CodeIgniter widened. New frameworks came along that did cater to the current practices, and
CodeIgniter started losing popularity.
The situation was not helped when the framework became seemingly abandoned by EllisLab as
they went through their own growing pains and didn’t have the resources they could provide to
Introduction 2

keep up with the community. A while later, they announced they were looking for a new home for
the framework, where it would have the resources it needed to grow again. In July 2014, nearly a
year later, it found its new home at the British Columbia Institute of Technology, one of Canada’s
top technology schools.
James Parry, one of the professors at BCIT, championed the project and led its revitalization. He
brought others on board to help guide the project.
Eventually, they decided that the PHP world had moved on so far beyond the traditions and practices
that past versions of CodeIgniter had found success in. A rewrite of the entire framework was needed.
One that would keep the spirit and feel of CodeIgniter 3 as much as possible, while bringing in
current PHP technologies and best practices so that it could fit in with today’s needs. The goal was a
modern, more secure, more flexible version of the PHP framework that many developers learned to
program with. It would not try to be everything to everyone. It never has, but it was believed there
was still a need for a framework that was lighter, more explicit, and more flexible than many of the
top frameworks were.
As the rewrite came close to completion, James Parry passed away due to lung cancer.
After 4+ years in development, CodeIgniter 4 was released to the world on his birthday, February
24, 2020.
Primer: Model-View-Controller (MVC)
What is MVC?
Model-View-Controller, or MVC, is a way of organizing your code into separate concerns. It helps
separate code to make it more flexible, more resilient, and more organized. This is the basic pattern
that CodeIgniter, and many other frameworks, use to structure your code.
With the exception of Controllers, none of this is enforced by the framework and you can build
classes in whatever manner best suits your application. Base classes for Models, and standard ways
of working with views, do provide a lot of built-in power and convenience at your disposal, though,
so you are encouraged to follow the pattern. That is, after all, why you chose to use a framework
instead of raw PHP, right?

Primary Class Purposes


Models are classes that interact with a data source. In CodeIgniter that means a database of one
form or another. It provides methods for saving and retrieving data to and from the database.
Views are files that determine what gets displayed to the client. These are typically PHP files
containing the HTML structure that can use variables provided to them to display. These can be
a full HTML page, or they can be broken up into smaller parts of the final page, called fragments.
They could also be used to format an XML response, etc, if that is how the data should be presented
to the client. This is much less frequently done.
Views should not do much logic, other than for determining display-related state. They should not
mix calls to the model to get data or anything else. Keep them simple and display focused.
Controllers are responsible for taking user input, ensuring it gets processed correctly, and returning
the output to the user. To follow best practices, the controller should be kept pretty light, with the
majority of business logic handled by other classes. The controller should do any basic error checking
on the incoming request, create instances of the business logic classes needed, and tell them what
needs to be done, getting the result back. This way the business logic can be shared among controllers,
and is easier to test.
A common problem I see less experienced developers try to do is include business logic functions
in the controller and attempt to call them from other controllers. Don’t do this. Instead, use another
class to handle the shared business logic for you and call that class from the controllers that need it.
Primer: Model-View-Controller (MVC) 4

Other Common Class Types


As you can see, these three main class types don’t cover every situation. You were even recommended
to use other classes in the discussion about Controllers. Let’s look at options here. First, we’ll see
what other uses CodeIgniter provides out of the box, and then we’ll look at ways you might consider
expanding as your application gets bigger.

CodeIgniter Extra Classes


Helpers contain simple functions that are not part of any classes. They typically contain short helper
functions to make doing common tasks simpler.
Entities represent a single item. Where the Model provides ways of interacting with an entire
database table, the Entity is used to represent a single row. They don’t know anything about the
database itself, so can be used for other tasks if you need their built-in functionality, but they are
designed to work with models.
Libraries is a catch-all term that is held over from previous versions of CodeIgniter where
autoloaders didn’t really exist. The framework provided a simple way to load in other classes without
hard-coding some requires and require-once everywhere. Libraries represented any class that
wasn’t a Model or Controller. Things have changed over the years and the two autoloaders that
CodeIgniter works with (its own internal one, and Composer¹) mean you can load the classes from
anywhere you want. You can still use the default Libraries folder for utility classes, or your business
logic if you want. You would just load them like any other class, with a namespace of App\Libraries.
Config files are now classes in themselves. That makes them simple to get an instance of it from
anywhere within the application.
¹https://getcomposer.org/
Primer: Namespaces
If you’re a seasoned pro using Namespaces in PHP, you can skip most of this chapter, though
skimming it for CodeIgniter-specific information might be helpful.

For many developers just starting out with namespaces, they can seem a bit of a mysterious thing.
Don’t let that scare you off, though. Whether you are brand new to PHP or have been using it for
years and have never needed to use them, you’ll pick it up quick.

The Simple Version


The simplest way to understand how namespaces are most commonly used in modern PHP is to
think of it as a way of mapping the classes to specific spots on the hard drive.
Obviously, there’s more to it than just that. Together with an Autoloader like CodeIgniter’s built-in
one, or Composer², allowing any file to be loaded without specifying where it lives on the disk is its
core functionality.
To assign a namespace to a class, you add a single line at the top of the file:

1 <?php namespace App\Models;

Given a class name of NewsModel, the fully-qualified class name would be App\Models\NewsModel.
As far as PHP is concerned that’s all it needs to know about it. It now knows how to dif-
ferentiate between this class and one with a similar name, but a different namespace, like
Acme\Models\NewsModel. Combined with an autoloader, though, it now also knows exactly where
on the disk to find that class. Let’s look at that from a default CodeIgniter application structure, and
see how that plays out.
A default CodeIgniter application is structured like this:

²https://getcomposer.org
Primer: Namespaces 6

1 app/
2 Config/
3 Controllers/
4 Database/
5 Filters/
6 Helpers/
7 Language/
8 Libraries/
9 Models/
10 ThirdParty/
11 Views/
12 system/

With the exception of the Config directory, everything under the application folder lives within the
App namespace. So files in the Controllers directory would have a namespace of App\Controllers.
Anything within the Models directory is namespaced under App\Models. For technical reasons we
won’t go into right here, the Config folder has its own namespace so any files in that directory are
simply namespaced Config.
If you want to get an instance of a class App\Models\NewsModel, you can do that very easily:

1 $model = new App\Models\NewsModel();

One thing you might be thinking is that this gets pretty wordy, requiring lots of typing. And you’re
absolutely correct. To minimize that you can specify at the top of the file what classes you are going
to use. Once you’ve done that you can simply give the main class name throughout the rest of your
code.

1 <?php namespace App\Controllers;


2
3 use App\Models\NewsModel;
4
5 class NewsController extends BaseController
6 {
7 public function index()
8 {
9 $model = new NewsModel();
10 }
11 }

If you have a couple of classes with the same base name that you need to use within the same file
you can assign an alias that can be used in that file only. This is done by typing as at the end of the
initial use statement followed by the name you want to refer to it as:
Primer: Namespaces 7

1 <?php
2 namespace App\Controllers;
3
4 use CodeIgniter\Controller;
5 use App\News\Manager as NewsManager;
6 use App\Blog\Manager as BlogManager;
7
8 class NewsControllerextends Controller
9 {
10 public function index()
11 {
12 $news = new NewsManager();
13 $blog = new BlogManager();
14 }
15 }

Case-sensitivity
One thing that you need to be aware of is that namespaces within CodeIgniter 4 are case-sensitive.
Any files or directories that the namespace maps to MUST match the case of the file or directory.
Looking at a NewModel again, it has the namespace of App\Models\NewsModel. This means the file
must be located at application\Models\NewsModel.php. Neither application\Models\newsModel.php
or application\models\newsmodel.php would be located by the framework. Conversely, a file at
application\Models\NewsModel.php could not use the namespace App\models since the case does
not match.

And that’s all you really need to know to start using namespaces in your CodeIgniter 4 application.
To recap:

• Namespaces help keep class names from colliding with others of the same name.
• Namespaces help CodeIgniter 4 find the classes on the disk so you don’t have to explicitly
require/include the files.
• Namespaces are case-sensitive and must match the case of the files and directories it maps to.

Going Deeper
Now let’s dig a little deeper into namespaces and how we can use them in our applications. We’ll
look at adding our own namespaces to CodeIgniter, integrating Composer - a third-party autoloader,
and more.
Primer: Namespaces 8

Your Code, Organized Your Way


CodeIgniter provides a default layout structure for your application, but you can easily add and
re-organize the code to fit your needs, with a very few restrictions.

Additional Directories

The easiest thing to do is to add a new directory to the application folder. You might do this to
keep all of your Entity classes together, similar to how Models are stored together by default. This
works out of the box. Simply create the directory, then any new files within that directory should
be namespaced to include the new directory name.
After creating a new directory at app/Entities to store our Entity classes, we would use the
following namespace for any Entities that you created: App\Entities. Whenever we need to get
an instance of that class we would use:

1 $entity = new App\Entities\NewsEntity();

That’s simple enough. Lets make some bigger changes.

Domain Mapping

Imagine a project that uses multiple classes for each domain of the application. If we are creating
a forum application, we might have a PostModel, a Post entity class, a PostManager (that acts as a
Repository class for Posts). We could completely change the structure of our application directory
to reflect this, organizing our business logic classes by the application domain, instead of the class
type. The application directory now might look more like:

1 app/
2 Config/
3 Controllers/
4 Database/
5 Domains/
6 Forums/
7 Forum.php
8 ForumManager.php
9 ForumModel.php
10 Posts/
11 Post.php
12 PostManager.php
13 PostModel.php
14 Threads/
15 Users/
Primer: Namespaces 9

16 Filters/
17 Helpers/
18 Language/
19 ThirdParty/
20 Views/

This keeps all classes related to the business logic or data together. In this example, the controllers
and views are kept in their default locations, but they could have been moved also. However, that
makes things a little more complicated since we are dealing with modules at that point, and will be
discussed in a later chapter.
Of course, all of the classes need to be correctly namespaced. So Forum.php would use the namespace
App\Domains\Forumswhile Post.php would use the namespace App\Domains\Posts. Notice that the
namespaces match the directory names and structure exactly.

New Namespaces

To organize any code outside of the app directory, the framework has to be told where to find the
new files so it can map the namespace correctly. This could be used to keep your business logic code
outside of the application directory, or to make a central place to store modules, for example. This
is simple and requires adding a single line to app/Config/Autoload.php, in the $psr4 block of the
constructor:

1 $psr4 = [
2 'Config' => APPPATH.'Config',
3 APP_NAMESPACE => APPPATH,
4 'App' => APPPATH,
5 'Tests\Support' => TESTPATH.'_support',
6 'Modules' => ROOTPATH.'modules', <- The new namespace
7 ];

In this example, a new namespace, Modules, has been defined. It can be found in a new directory
modules that sits on the same level as the application and system directories. The constant ROOTPATH
used here specifies the main directory that the project sits in. Other constants exist that can be used,
also, though ROOTPATH and APPPATH will be the two most likely to be used:

• ROOTPATH is the directory that holds the app and system directories
• APPPATH is the app directory
• TESTPATH is the tests directory
• BASEPATH is the system directory
• WRITEPATH is the writable directory
Primer: Namespaces 10

CodeIgniter can use 2 different autoloaders. The first is the one built into the framework,
which is what we will discuss here. The second is Composer. CodeIgniter has its own for
a couple of reasons. The first is to remove the dependence on Composer, something the
community was very adamant about when work on version 4 was getting started. The
second reason is that it allows us to do some tricks throughout the system that help provide
additional flexibility and allow non-class files to be “namespaced”, like views, helpers, etc.
Primer: Dependency Injection
Even more so than namespaces, Dependency Injection seems to have gotten a massive cloud of
confusion based around it. In this chapter I hope to clear up the confusion, and show you what it is,
how it works, and how I recommend using it within your own applications.

What is it?
Dependency Injection (a.k.a. DI), at its core, simply means that any dependencies for your class
should be passed into it, instead of creating the dependency within the class itself.
First an example of a non-DI way to do it:

1 class UserManager {
2 public function list() {
3 $model = new UserModel();
4
5 return $model->findAll();
6 }
7 }

In this example, we grab a new instance of the UserModel class within our UserManager class. This
is fine for simple code, but breaks down when we start getting more advanced.
This is especially true when you start testing your code. If you were trying to test all of your
UserManager methods, you would end up needing to setup the testing database, create the test tables,
add in any sample data, all before running each test. While that type of testing has things going for
it, it can be slow. Instead, you might want to create a mock of the class where you can define how
the class reacts and never hit the database, making your tests run much faster. However, if you are
creating a new instance of the class within your UserManager you cannot easily do that.

How to do it
To simplify your life while testing, you should instead inject the dependencies for UserManager, in
this case the UserModel class, into the UserManager. This allows you to create a mocked-up version
of the UserModel and pass that mock into the UserManager within all of your tests. This is most often
done within the constructor, like:
Primer: Dependency Injection 12

1 class UserManager {
2 protected $userModel;
3
4 public function __construct($userModel) {
5 $this->userModel = $userModel;
6 }
7
8 public function list() {
9 return $this->userModel->findAll();
10 }
11 }

How to use it
Now that you understand that basics, let’s look at how to use this in your day to day.
Let’s say you are building a classifieds ad app. Odds are you would need to collect some functionality
together in a single class related to the ads. Functions that make it easier or more semantic to work
with the AdsModel.

1 <?php namespace App\Libraries;


2
3 use CodeIgniter\Model;
4 use App\Models\AdsModel;
5
6 class AdsManager
7 {
8 /**
9 * @var AdsModel
10 */
11 protected $model;
12
13 public function __construct(Model $ads) {
14 $this->model = $ads;
15 }
16 }

We now have a skeleton class that we can expand for anything that needs to do more than a model
does by itself. When we need this, we pass the model in with it. Within our constructor it might
look like this:
Primer: Dependency Injection 13

1 use App\Libraries\AdsManager;
2 use App\Models\AdsModel;
3
4 class AdsController
5 {
6 public function localAds(string $region) {
7 $adsMan = new AdsManager(model(AdsModel::class));
8
9 return view('ads/local', [
10 'ads' => $adsMan->localAds($region),
11 ]);
12 }
13 }

Here we create a new instance of the AdsManager. We create an instance of the AdsModel with
the model helper and pass it in through the constructor. While this example is not too bad to type
every time, it can get cumbersome as the number of dependencies grow. There’s a couple of ways
to handle it, but CodeIgniter provides a concept called Services that you’ll read all about in the next
chapter that will make this simple to handle in your application.

The model() helper will return an instance of the model class. It scans across all namespaces
in Config\Autoload.php. By default, it will always return the same instance so you can use
it anywhere in your app and always get the same instance of the class. Very handy stuff.

Why Controllers are Exempt


If DI is so great, why can’t we do like other frameworks do and simply list the class within the
constructor and have it auto-magically create it for us and inject it. We could have. And we almost
did.
Back when I first started helping with the framework I wrote what I thought was a pretty fine start
at a DI container, or IoC container, or whatever you’d like to think of it as. And it would do just that
- analyze the method’s arguments and search out and create new instances of the class requested,
passing it in the constructor. I felt pretty good.
Then I read a blog post ³ by Anthony Ferrara talking about the difference of simple vs easy and how
that relates to risk and change within your apps in the future. Within it, he specifically mentioned
the case of DI Containers, and how they were easy, but they were black box of stuff happening in
the background that was often difficult to understand. Why couldn’t we make this simple, instead?
That resonated with me a lot at the time and I eventually went with the Services concept I’ll talk
about next. Services are a simple class that returns an instance of the class you want. It handles
³https://blog.ircmaxell.com/2015/11/simple-easy-risk-and-change.html
Primer: Dependency Injection 14

injecting dependencies, returning shared or single instances. And it does it all in plain code that
anyone can easily read and understand. If your app changes frameworks down the road, or you need
to customize the core of the framework, you only change a couple of lines of code. It’s simply. But
it’s a slight amount of work more than easy. But not much. The prize, though, is that it’s transparent
and explicit.
Getting back to Controllers, though. If you wade through discussion/arguments online about DI,
Ioc, Service pattern, etc. there’s a lot of rhetoric on all sides on whether it’s right or wrong to create
classes within any of your classes, which includes controller. Here’s the way I look at it. You may
have a different viewpoint and that’s great - it’s what brings on change and keeps the world moving.
Looking back at Chapter 2, we saw that “controllers are responsible for taking user input, ensuring
it gets processed correctly, and returning the output to the user.” In order to do that they must
orchestrate all the pieces. They load the proper models, libraries, etc. They collect user input, pass
it into those models/libraries, and collect the results. Then they organize that result into the format
the original client wants to see it in, whether that’s a view, a JSON object, a chunk of XML like an
RSS feed, or anything else.
While gathering the tools they need to perform these actions, they can either do it in a way that
is explicit and makes it simple for the developer to understand and trace what’s going on, or they
can do it in a way that is nicer to read, but much more complex and obtuse. I prefer the code to be
simpler to read, and easier for the next developer to understand what is going on. I prefer that the
Controller creates the classes in plain site instead of having it injected through the constructor or a
method’s argument list.
Either option is fine, and works in the long run. I’m not here to say one was is better than others.
Just that this is why CodeIgniter 4 is the way it is.
Primer: Services
Services are a core part of CodeIgniter 4, serving as convenient shortcuts to setting up a class and
its dependencies as well as providing a simple method for overriding most of the core framework
classes. They were mentioned in the previous chapter, but we will dive deeper into them in this
chapter.

What Are They?


Services are simple: it’s a way to create an instance of a class and all of its dependencies. It can either
provide a brand new instance of the class, or a shared instance that will always be returned when
that service is called. A shared instance is the default, as that allows simple class re-use throughout
the framework.
Almost all of the system classes are available as a service. This allows you to easily grab, for example,
the IncomingRequest instance that represents this request no matter where you are in the code.
And don’t worry, if you’re into testing, it provides a simple way to insert your own version for use
during tests.

How to Use Them


You can use the helper function, service(), to load any service, whether it’s a provided one in the
core framework, one you create yourself, or one provided in a third-party module.
If you take a look at system/Config/Services.php you’ll find a lot of static functions in the Services
class. All of these are services that you can call. For example, if you want to grab the cache service,
you would do this:

1 $cache = service('cache');
2 $value = $cache->get('key');

You can chain the call together with the functionality you need if you only need to use the class
once:

1 service('cache')->set('key', $value);

Many of the services take optional parameters. For example, the cache service can take a configura-
tion class instance as its first argument, allowing you alter how the class works. Remember, though,
that by default services return a shared instance.
Primer: Services 16

1 $cache1 = service('cache');
2
3 $config = new Config\Cache();
4 $cache2 = service('cache', $config);

In this example, you might expect that $cache1 and $cache2 are different, but they are not. when
$cache1 is created its instance is saved. Whenever another call to service()‘ is called,
that original instance is returned. It does not cache multiple versions based on their arguments. It is
possible to get a new instance using the new parameters, though:

1 $cache1 = service('cache');
2
3 $config = new Config\Cache();
4 $cache2 = service('cache', $config, true);
5 // or
6 $cache2 = single_service('cache', $config);

The last argument in service() determines whether it is a shared instance or not. If true, it returns a
new instance. If false, it returns a shared one. You can also use the helper method single_service()
which functions just like service() with the exception that it always returns a new instance.

When to Use Them


Services can be tempting to use all the time, but that is not always necessary. Many times you can
get away just fine with creating a new class instance the basic PHP way, and passing that to the
classes that need it:

1 $users = new UserModel();


2 $tickets = new BoxOffice($users);

This is cleaner and more explicit, allowing any future developers (or yourself after having worked
on another project for 6 months) to easily follow what is happening and where the pieces come
from.
In general, there are two reasons to use your own services:

1. You have a class that needs some fairly complex setup that would be too much pain to recreate
every time.
2. An instance needs to be used in multiple places where it’s not easy to pass the dependences in
directly.

That is not to say those are the only reasons you might want to use them, but they are the starting
place to ask yourself when considering using a service. If either of those cases matches then go for
it. If they don’t exactly match up, you might try another way first.
Primer: Services 17

Creating Your Own


When you do decide to use a service of your own, you’ll find them to be pretty simple to create. Open
up the file, app/Config/Services.php, copy the example method that is in the file, and modify it for
your own code.

1 public static function boxoffice($getShared = true)


2 {
3 if ($getShared)
4 {
5 return static::getSharedInstance('boxoffice');
6 }
7
8 $users = new UserModel();
9
10 return new \App\Libraries\BoxOffice($users);
11 }

Hopefully, most of that is pretty clear by now. If $getShared then it runs it through the base class’
getSharedInstance() method which checks if we’ve already cached an instance of this class. If we
have it returns the cached instance. If a cached instance does not exist, then it calls this method
again, with $getShared set to false. That’s when the instance creation code you write does its thing
and returns a new instance. The one thing to watch out for is to ensure you pass in the name of the
method as the first argument to getSharedInstance(). That’s how it knows which method to call.
If your service needs the ability to have arguments passed in it when created, then you add those
arguments first in the method. The $getShared argument must always be the last argument or things
will not work correctly.

1 public static function boxoffice($payments = null, $getShared = true)


2 {
3 if ($getShared)
4 {
5 return static::getSharedInstance('boxoffice', $payments);
6 }
7
8 $users = new UserModel();
9 $payments = $payments === null ? new Stripe() : $payments;
10
11 return new \App\Libraries\BoxOffice($users, $payments);
12 }

Finally, make sure to pass in each of the new arguments in $getSharedInstance() call, as shown
above.
Primer: Services 18

Extending the Core Framework


Since most of the pieces of the framework have a service setup for them, all you need to do to
extend or replace a core class is to copy the services’ method from system/Config/Services.php
into app/Config/Services.php and modify it to return an instance of your class instead.

1 public static function typography(bool $getShared = true)


2 {
3 if ($getShared)
4 {
5 return static::getSharedInstance('typography');
6 }
7
8 return new \PHP_Typography\PHP_Typography();
9 }

Once done, your class will always be used in place of the system class. In many cases the system
will expect the class returned to implement a specific interface so be sure to check out the class that
you’re replacing to see if it implements an interface that you’ll need to follow.
Project 1 - Brochure Site Take 1
We are going to start simple, and get the basics under our belt with this first project. In future
chapters we’ll expand on the concepts presented in the earlier ones. The projects themselves will
change in most chapters. In the first two, though, we will look at two different ways to build the
same simple, one page brochure site.

You can find all sample projects described in this book over at GitHub⁴ if you want to view
the full codebase.

Routes
Routes define what controller is used when a user visits a specific URL. They are configured within
the file, app/Config/Routes.php. For each URL in your site, you define a single line of code.

1 $routes->get('/', 'Home::index');

We will get into more details on that in the next chapter, as there are two ways that you can do
routing. The first is the same that previous versions of CodeIgniter have used, where it automatically
routes a URL to a controller and method, based upon matching the names. This can be handy, but I
recommend staying away from it. But having to write out each route is too tedious, you say? It only
takes a few seconds to create a route, and if your site needs hundreds, you can likely restructure
something to work with only a few dozen routes, which is manageable.
For our examples, we’re going to turn the auto-routing feature off, and do it manually. So edit
app/Config/Routes.php and change setAutoRoute to false:

1 $routes->setAutoRoute(false);

Displaying Our Site


We won’t use a controller here to display our single-page brochure website. Instead, we’ll use a
simple closure to define everything in one spot. Modify the default homepage route that is in the
Routes file to the following:
⁴https://github.com/lonnieezell/ci4-foundations
Project 1 - Brochure Site Take 1 20

1 $routes->get('/', function() {
2 echo view('home');
3 });

With the exception of the actual content to display, this is all that is needed to display our site. It
states that if someone visits /, then it will run the given function, which simply displays our content.
The view() helper method is one of a number of helper functions⁵ that are always available. They
make it simple to access common functionality without loading up instances of the required classes
and setting them up.
The first argument it takes is the name of the file to grab and render out. By default, it expects to
find the files in the app/Views folder. So, this command would look for the file app/Views/home.php.
The view file itself can be any combination of PHP and HTML needed to display your content. At
it’s simplest, the view might look something like:

1 <!doctype html>
2 <html>
3 <head>
4 <title>Acme, Inc</title>
5 </head>
6 <body>
7 <h1>Welcome to Acme, Inc</h1>
8 <p>Purveyor of fine rockets, magnets, and tools.</p>
9 </body>
10 </html>

Even on simple brochure sites, though, we might need to display dynamic data within the view itself.
For this example, we’ll pass some simple, faked data into the view to demonstrate how the data gets
there and can be used. We’ll cover more realistic details a little later.

1 $routes->get('/', function() {
2 echo view('home', [
3 'siteName' => 'Acme, Inc',
4 'categories' => ['rockets', 'magnets', 'tools']
5 ]);
6 });

As the second argument to the view() command, we can pass in an array of data that should be
available within the view for us to work with. The keys are the names the data will be accessed as
within the view. The updated view would look like this:

⁵https://codeigniter.com/user_guide/general/common_functions.html
Project 1 - Brochure Site Take 1 21

1 <!doctype html>
2 <html>
3 <head>
4 <title><?= $siteName ?></title>
5 </head>
6 <body>
7 <h1>Welcome to <?= $siteName ?></h1>
8 <p>Purveyor of fine
9 <?php foreach($categories as $cat) : ?>
10 <?= $cat .', ' ?>
11 <?php endforeach ?>, and more.</p>
12 </body>
13 </html>

Here we use the php shorthand syntax (<?=) to echo the content out using the key of the array we
passed in before. For the categories we simply loop over the content array as we would loop over
any other array.

This has provided an overview of how to take a visitor and display a page. With this type of a
setup you could do any brochure site that doesn’t need any special management abilities. Hopefully,
you’re site would look better than this one, though. :)

You might also like