Packages are the recommended way of extending the functionality provided by Laravel 4. They’re nothing more than Composer libraries with some particular bootstrapping code.
This chapter follows on from a previous chapter which covered the basics of creating a deployment process for your applications. It goes into detail about creating a Laravel 4 installation; so you should be familiar with it before spending a great deal of time in this one. You’ll also find the source-code we created there to be a good basis for understanding the source code of this chapter.
I learned the really technical bits of this chapter from reading Taylor’s book. If you take away just one thing from this it should be to get and read it.
While the previous chapter shows you how to create a Laravel 4 project; we need to do a bit more work in this area if we are to understand one of the fundamental ways in which packages differ from application code.
Laravel 3 had the concept of bundles; which were downloadable folders of source code. Laravel 4 extends this idea, but instead of rolling its own download process, it makes use of Composer. Composer should not only be used to create new Laravel 4 installations; but also as a means of adding packages to existing Laravel 4 applications.
Laravel 4 packages are essentially the same things as normal Composer libraries. They often utilise functionality built into the Laravel 4 framework, but this isn’t a requirement of packages in general.
It follows that the end result of our work today is a stand-alone Composer library including the various deployment tools we created last time. For now, we will be placing the package code inside our application folder to speed up iteration time.
One of the darlings of modern PHP development is Dependency Injection. PHP developers have recently grown fond of unit testing and class decoupling. I’ll explain both of these things shortly; but it’s important to know that they are greatly improved by the use of dependency injection.
Let’s look at some non-injected dependencies, and the troubles they create for us.
1
<?
php
2
3
class
Archiver
4
{
5
protected
$database
;
6
7
public
function
__construct
()
8
{
9
$this
->
database
=
Database
::
connect
(
10
"host"
,
11
"username"
,
12
"password"
,
13
"schema"
14
);
15
}
16
17
public
function
archive
()
18
{
19
$entries
=
$this
->
database
->
getEntries
();
20
21
foreach
(
$entries
as
$entry
)
22
{
23
if
(
$entry
->
aggregated
)
24
{
25
$entry
->
archive
();
26
}
27
}
28
}
29
}
30
31
$archiver
=
new
Archiver
();
32
$archiver
->
archive
();
Assuming, for a moment, that Database has been defined to pull all entries in such a way that they have an aggregated property and an archive() method; this code should be straightforward to follow.
The Archiver constructor initialises a database connection and stores a reference to it within a protected property. The archive() method iterates over the entries and archives them if they are already aggregated. Easy stuff.
Not so easy to test. The problem is that testing the business logic means testing the database connection and the entry instances as well. They are dependencies to any unit test for this class.
Furthermore; the Archiver class needs to know that these things exist and how they work. It’s not enough just to know that the database instance is available or that the getEntries() method returns entries. If we have thirty classes all depending on the database; something small (like a change to the database password) becomes a nightmare.
Dependency injection takes two steps, in PHP. The first is to declare dependencies outside of classes and pass them in:
1
<?
php
2
3
class
Archiver
4
{
5
protected
$database
;
6
7
public
function
__construct
(
Database
$database
)
8
{
9
$this
->
database
=
$database
;
10
}
11
12
// ...then the archive() method
13
}
14
15
$database
=
Database
::
connect
(
16
"host"
,
17
"username"
,
18
"password"
,
19
"schema"
20
);
21
22
$archiver
=
new
Archiver
(
$database
);
23
$archiver
->
archive
();
This may seem like a small, tedious change but it immediately enables independent unit testing of the database and the Archiver class.
The second step to proper dependency injection is to abstract the dependencies in such a way that they provide a minimum of functionality. Another way of looking at it is that we want to reduce the impact of changes or the leaking of details to classes with dependencies:
1
<?
php
2
3
interface
EntryProviderInterface
4
{
5
public
function
getEntries
();
6
}
7
8
class
EntryProvider
implements
EntryProviderInterface
9
{
10
protected
$database
;
11
12
public
function
__construct
(
Database
$database
)
13
{
14
$this
->
database
=
$database
;
15
}
16
17
public
function
getEntries
()
18
{
19
// ...get the entries from the database
20
}
21
}
22
23
class
Archiver
24
{
25
protected
$provider
;
26
27
public
function
__construct
(
28
EntryProviderInterface
$provider
29
)
30
{
31
$this
->
provider
=
$provider
;
32
}
33
34
// ...then the archive() method
35
}
36
37
$database
=
Database
::
connect
(
38
"host"
,
39
"username"
,
40
"password"
,
41
"schema"
42
);
43
44
$provider
=
new
EntryProvider
(
$database
);
45
$archiver
=
new
Archiver
(
$provider
);
46
$archiver
->
archive
();
Woah Nelly! That’s a lot of code when compared to the first snippet; but it’s worth it. Firstly; we’re exposing so few details to the Archiver class that the entire entry dependency could be replaced in a heartbeat. This is possible because we’ve moved the database dependency out of the Archiver and into the EntryProvider.
We’re also type-hinting an interface instead of a concrete class; which lets us swap the concrete class out with anything else that implements EntryProviderInterface. The concrete class can fetch the entries from the database, or the filesystem or whatever.
We can test the EntryProvider class by swapping in a fake Database instance. We can test the Archiver class by swapping in a fake EntryProvider instance.
So, to recap the requirements for dependency injection:
“When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.
What you should be doing is stating a need, ‘I need something to drink with lunch,’ and then we will make sure you have something when you sit down to eat.”
— John Munsch
Inversion of Control (IoC) is the name given to the process that involves assembling class instances (and the resolution of class instances in general) within a container or registry. Where the registry pattern involves defining class instances and then storing them in some global container; IoC involves telling the container how and where to find the instances so that they can be resolved as required.
This naturally aids dependency injection as class instances adhering to abstracted requirements can easily be resolved without first creating. To put it another way; if our classes adhere to certain requirements (via interfaces) and the IoC container can resolve them to class instances, then we don’t have to do it beforehand.
The easiest way to understand this is to look at some code:
1
<?
php
2
3
interface
DatabaseInterface
4
{
5
// ...
6
}
7
8
class
Database
implements
DatabaseInterface
9
{
10
// ...
11
}
12
13
interface
EntryProviderInterface
14
{
15
// ...
16
}
17
18
class
EntryProvider
implements
EntryProviderInterface
19
{
20
protected
$database
;
21
22
public
function
__construct
(
DatabaseInterface
$database
)
23
{
24
$this
->
database
=
$database
;
25
}
26
27
// ...
28
}
29
30
interface
ArchiverInterface
31
{
32
// ...
33
}
34
35
class
Archiver
implements
ArchiverInterface
36
{
37
protected
$provider
;
38
39
public
function
__construct
(
40
EntryProviderInterface
$provider
41
)
42
{
43
$this
->
provider
=
$provider
;
44
}
45
46
// ...
47
}
48
49
$database
=
new
Database
();
50
$provider
=
new
EntryProvider
(
$database
);
51
$archiver
=
new
Archiver
(
$provider
);
This shallowly represents a dependency (injection) chain. The last three lines are where the problem starts to become clear; the more we abstract our dependencies, the more “bootstrapping” code needs to be done every time we need the Archiver class.
We could abstract this by using Laravel 4’s IoC container:
1
<?
php
2
3
// ...define classes
4
5
App
::
bind
(
"DatabaseInterface"
,
function
()
{
6
return
new
Database
();
7
});
8
9
App
::
bind
(
"EntryProviderInterface"
,
function
()
{
10
return
new
EntryProvider
(
11
App
::
make
(
"DatabaseInterface"
)
12
);
13
});
14
15
App
::
bind
(
"ArchiverInterface"
,
function
()
{
16
return
new
Archiver
(
17
App
::
make
(
"EntryProviderInterface"
)
18
);
19
});
20
21
$archiver
=
App
::
make
(
"ArchiverInterface"
);
These extra nine lines (using the App::bind() and App::make() methods) tell Laravel 4 how to find/make new class instances, so we can get to the business of using them!
The main purpose of services providers is to collect and organise the bootstrapping requirements of your package. They’re not strictly a requirement of package development; but rather a Really Good Idea™.
There are three big things to service providers. The first is that they are registered in a common configuration array (in app/config/app.php):
1
'providers'
=>
array
(
2
'Illuminate\Foundation\Providers\ArtisanServiceProvider'
,
3
'Illuminate\Auth\AuthServiceProvider'
,
4
'Illuminate\Cache\CacheServiceProvider'
,
5
// ...
6
'Illuminate\Validation\ValidationServiceProvider'
,
7
'Illuminate\View\ViewServiceProvider'
,
8
'Illuminate\Workbench\WorkbenchServiceProvider'
,
9
),
You can also add your own service providers to this list; as many packages will recommend you do. There there’s the register() method:
1
<?
php
namespace
Illuminate\Cookie
;
2
3
use
Illuminate\Support\ServiceProvider
;
4
5
class
CookieServiceProvider
extends
ServiceProvider
{
6
7
/**
8
* Register the service provider.
9
*
10
* @return void
11
*/
12
public
function
register
()
13
{
14
$this
->
app
[
'cookie'
]
=
$this
->
app
->
share
(
function
(
$app
)
15
{
16
$cookies
=
new
CookieJar
(
17
$app
[
'request'
],
18
$app
[
'encrypter'
]
19
);
20
21
$config
=
$app
[
'config'
][
'session'
];
22
23
return
$cookies
->
setDefaultPathAndDomain
(
24
$config
[
'path'
],
25
$config
[
'domain'
]
26
);
27
});
28
}
29
30
}
When we take a look at the register() method of the CookieServiceProvider class; we can see a call to $this->app->share() which is similar to the App::bind() method but instead of creating a new class instance every time it’s resolved in the IoC container; share() wraps a callback so that it’s shared with every resolution.
The name of the register() method explains exactly what it should be used for; registering things with the IoC container (which App extends). If you need to do other bootstrapping stuff then the method you need to use is boot():
1
public
function
boot
()
2
{
3
Model
::
setConnectionResolver
(
$this
->
app
[
'db'
]);
4
Model
::
setEventDispatcher
(
$this
->
app
[
'events'
]);
5
}
This boot() method sets two properties on the Model class. The same class also has a register method but these two settings are pet for the boot method.
Now that we have some tools to help us create packages; we need to port our code over from being application code to being a package. Laravel 4 provides a useful method for creating some structure to our package:
1
php artisan workbench Formativ/Deployment
You’ll notice a new folder has been created, called workbench. Within this folder you’ll find an assortment of files arranged in a similar way to those in the vendor/laravel/framework/src/Illuminate/* folders.
We need to break all of the business logic (regarding deployment) into individual classes, making use of the IoC container and dependency injection, and make a public API.
To recap; the commands we need to abstract are:
Many of these were cluttering up the list of commands which ship with Laravel. Our organisation should collect all of these in a common “namespace”.
The first step is to create a few contracts (interfaces) for the API we want to expose through our package:
1
<?
php
2
3
namespace
Formativ\Deployment\Asset
;
4
5
interface
ManagerInterface
6
{
7
public
function
add
(
$source
,
$target
);
8
public
function
remove
(
$target
);
9
public
function
combine
();
10
public
function
minify
();
11
}
1
<?
php
2
3
namespace
Formativ\Deployment\Asset
;
4
5
interface
WatcherInterface
6
{
7
public
function
watch
();
8
}
1
<?
php
2
3
namespace
Formativ\Deployment
;
4
5
interface
DistributionInterface
6
{
7
public
function
prepare
(
$source
,
$target
);
8
public
function
sync
();
9
}
1
<?
php
2
3
namespace
Formativ\Deployment
;
4
5
interface
MachineInterface
6
{
7
public
function
getEnvironment
();
8
public
function
setEnvironment
(
$environment
);
9
}
The methods required by these interfaces encompass the functionality our previous commands provided for us. The next step is to fulfil these contracts with concrete class implementations:
1
<?
php
2
3
namespace
Formativ\Deployment\Asset
;
4
5
use
Formativ\Deployment\MachineInterface
;
6
use
Illuminate\Filesystem\Filesystem
;
7
8
class
Manager
9
implements
ManagerInterface
10
{
11
protected
$files
;
12
13
protected
$machine
;
14
15
protected
$assets
=
[];
16
17
public
function
__construct
(
18
Filesystem
$files
,
19
MachineInterface
$machine
20
)
21
{
22
$this
->
files
=
$files
;
23
$this
->
machine
=
$machine
;
24
}
25
26
public
function
add
(
$source
,
$target
)
27
{
28
// ...add files to $assets
29
}
30
31
public
function
remove
(
$target
)
32
{
33
// ...remove files from $assets
34
}
35
36
public
function
combine
()
37
{
38
// ...combine assets
39
}
40
41
public
function
minify
()
42
{
43
// ...minify assets
44
}
45
}
1
<?
php
2
3
namespace
Formativ\Deployment\Asset
;
4
5
use
Formativ\Deployment\MachineInterface
;
6
use
Illuminate\Filesystem\Filesystem
;
7
8
class
Watcher
9
implements
WatcherInterface
10
{
11
protected
$files
;
12
13
protected
$machine
;
14
15
public
function
__construct
(
16
Filesystem
$files
,
17
MachineInterface
$machine
18
)
19
{
20
$this
->
files
=
$files
;
21
$this
->
machine
=
$machine
;
22
}
23
24
public
function
watch
()
25
{
26
// ...watch assets for changes
27
}
28
}
1
<?
php
2
3
namespace
Formativ\Deployment
;
4
5
use
Formativ\Deployment\MachineInterface
;
6
use
Illuminate\Filesystem\Filesystem
;
7
8
class
Distribution
9
implements
DistributionInterface
10
{
11
protected
$files
;
12
13
protected
$machine
;
14
15
public
function
__construct
(
16
Filesystem
$files
,
17
MachineInterface
$machine
18
)
19
{
20
$this
->
files
=
$files
;
21
$this
->
machine
=
$machine
;
22
}
23
24
public
function
prepare
(
$source
,
$target
)
25
{
26
// ...copy + clean files for distribution
27
}
28
29
public
function
sync
()
30
{
31
// ...sync distribution files to remote server
32
}
33
}
1
<?
php
2
3
namespace
Formativ\Deployment
;
4
5
use
Illuminate\Filesystem\Filesystem
;
6
7
class
Machine
8
implements
MachineInterface
9
{
10
protected
$environment
;
11
12
protected
$files
;
13
14
public
function
__construct
(
Filesystem
$files
)
15
{
16
$this
->
files
=
$files
;
17
}
18
19
public
function
getEnvironment
()
20
{
21
// ...get the current environment
22
}
23
24
public
function
setEnvironment
(
$environment
)
25
{
26
// ...set the current environment
27
}
28
}
The main to note about these implementations is that they use dependency injection instead of creating class instances in their constructors. As I pointed out before; we’re not going to go over the actually implementation code, but feel free to port it over from the application-based commands.
As you can probably tell; I’ve combined related functionality into four main classes. This becomes even more apparent when you consider what the service provider has become:
1
<?
php
2
3
namespace
Formativ\Deployment
;
4
5
use
App
;
6
use
Illuminate\Support\ServiceProvider
;
7
8
class
DeploymentServiceProvider
9
extends
ServiceProvider
10
{
11
protected
$defer
=
true
;
12
13
public
function
register
()
14
{
15
App
::
bind
(
"deployment.asset.manager"
,
function
()
16
{
17
return
new
Asset\Manager
(
18
App
::
make
(
"files"
),
19
App
::
make
(
"deployment.machine"
)
20
);
21
});
22
23
App
::
bind
(
"deployment.asset.watcher"
,
function
()
24
{
25
return
new
Asset\Watcher
(
26
App
::
make
(
"files"
),
27
App
::
make
(
"deployment.machine"
)
28
);
29
});
30
31
App
::
bind
(
"deployment.distribution"
,
function
()
32
{
33
return
new
Distribution
(
34
App
::
make
(
"files"
),
35
App
::
make
(
"deployment.machine"
)
36
);
37
});
38
39
App
::
bind
(
"deployment.machine"
,
function
()
40
{
41
return
new
Machine
(
42
App
::
make
(
"files"
)
43
);
44
});
45
46
App
::
bind
(
"deployment.command.asset.combine"
,
function
()
47
{
48
return
new
Command\Asset\Combine
(
49
App
::
make
(
"deployment.asset.manager"
)
50
);
51
});
52
53
App
::
bind
(
"deployment.command.asset.minify"
,
function
()
54
{
55
return
new
Command\Asset\Minify
(
56
App
::
make
(
"deployment.asset.manager"
)
57
);
58
});
59
60
App
::
bind
(
"deployment.command.asset.watch"
,
function
()
61
{
62
return
new
Command\Asset\Watch
(
63
App
::
make
(
"deployment.asset.manager"
)
64
);
65
});
66
67
App
::
bind
(
"deployment.command.distribute.prepare"
,
function
()
68
{
69
return
new
Command\Distribute\Prepare
(
70
App
::
make
(
"deployment.distribution"
)
71
);
72
});
73
74
App
::
bind
(
"deployment.command.distribute.sync"
,
function
()
75
{
76
return
new
Command\Distribute\Sync
(
77
App
::
make
(
"deployment.distribution"
)
78
);
79
});
80
81
App
::
bind
(
"deployment.command.machine.add"
,
function
()
82
{
83
return
new
Command\Machine\Add
(
84
App
::
make
(
"deployment.machine"
)
85
);
86
});
87
88
App
::
bind
(
"deployment.command.machine.remove"
,
function
()
89
{
90
return
new
Command\Machine\Remove
(
91
App
::
make
(
"deployment.machine"
)
92
);
93
});
94
95
$this
->
commands
(
96
"deployment.command.asset.combine"
,
97
"deployment.command.asset.minify"
,
98
"deployment.command.asset.watch"
,
99
"deployment.command.distribute.prepare"
,
100
"deployment.command.distribute.sync"
,
101
"deployment.command.machine.add"
,
102
"deployment.command.machine.remove"
103
);
104
}
105
106
public
function
boot
()
107
{
108
$this
->
package
(
"formativ/deployment"
);
109
include
__DIR__
.
"/../../helpers.php"
;
110
include
__DIR__
.
"/../../macros.php"
;
111
}
112
113
public
function
provides
()
114
{
115
return
[
116
"deployment.asset.manager"
,
117
"deployment.asset.watcher"
,
118
"deployment.distribution"
,
119
"deployment.machine"
,
120
"deployment.command.asset.combine"
,
121
"deployment.command.asset.minify"
,
122
"deployment.command.asset.watch"
,
123
"deployment.command.distribute.prepare"
,
124
"deployment.command.distribute.sync"
,
125
"deployment.command.machine.add"
,
126
"deployment.command.machine.remove"
127
];
128
}
129
}
The first four bind() calls bind our four main classes to the IoC container. The remaining bind() methods bind the artisan commands we still have to create (to replace those we make last time).
There’s also a call to $this->commands(); which registers commands (bound to the IoC container) with artisan. Finally; we define the provides() method, which coincides with the $defer = true property, to inform Laravel which IoC container bindings are returned by this service provider. By setting $defer = true; we’re instructing Laravel to not immediately load the provided classes and commands, but rather wait until they are required.
This is the first time we’re using the boot() method. The call to $this->package() is so that we can have views, language files and other resources mapped to our package. We would be able to call them by prefixing the view names, language keys etc. with the name of the package. We’re not going to be using that this time round; but it’s important to know how.
We also include helpers.php and macros.php in the boot method.
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Asset
;
4
5
use
Formativ\Deployment\Asset\ManagerInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Combine
11
extends
Command
12
{
13
protected
$name
=
"deployment:asset:combine"
;
14
15
protected
$description
=
"Combines multiple resource files."
;
16
17
protected
$manager
;
18
19
public
function
__construct
(
ManagerInterface
$manager
)
20
{
21
parent
::
__construct
();
22
$this
->
manager
=
$manager
;
23
}
24
25
// ...
26
}
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Asset
;
4
5
use
Formativ\Deployment\Asset\ManagerInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Minify
11
extends
Command
12
{
13
protected
$name
=
"deployment:asset:minify"
;
14
15
protected
$description
=
"Minifies multiple resource files."
;
16
17
protected
$manager
;
18
19
public
function
__construct
(
ManagerInterface
$manager
)
20
{
21
parent
::
__construct
();
22
$this
->
manager
=
$manager
;
23
}
24
25
// ...
26
}
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Asset
;
4
5
use
Formativ\Deployment\Asset\ManagerInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Watch
11
extends
Command
12
{
13
protected
$name
=
"deployment:asset:watch"
;
14
15
protected
$description
=
"Watches files for changes."
;
16
17
protected
$manager
;
18
19
public
function
__construct
(
ManagerInterface
$manager
)
20
{
21
parent
::
__construct
();
22
$this
->
manager
=
$manager
;
23
}
24
25
// ...
26
}
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Distribute
;
4
5
use
Formativ\Deployment\DistributionInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Prepare
11
extends
Command
12
{
13
protected
$name
=
"deployment:distribute:prepare"
;
14
15
protected
$description
=
"Prepares the distribution folder."
;
16
17
protected
$distribution
;
18
19
public
function
__construct
(
20
DistributionInterface
$distribution
21
)
22
{
23
parent
::
__construct
();
24
$this
->
distribution
=
$distribution
;
25
}
26
27
// ...
28
}
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Distribute
;
4
5
use
Formativ\Deployment\DistributionInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Sync
11
extends
Command
12
{
13
protected
$name
=
"deployment:distribute:sync"
;
14
15
protected
$description
=
"Syncs changes to a target."
;
16
17
protected
$distribution
;
18
19
public
function
__construct
(
20
DistributionInterface
$distribution
21
)
22
{
23
parent
::
__construct
();
24
$this
->
distribution
=
$distribution
;
25
}
26
27
// ...
28
}
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Machine
;
4
5
use
Formativ\Deployment\MachineInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Add
11
extends
Command
12
{
13
protected
$name
=
"deployment:machine:add"
;
14
15
protected
$description
=
"Adds the current machine to an environment."
;
16
17
protected
$machine
;
18
19
public
function
__construct
(
MachineInterface
$machine
)
20
{
21
parent
::
__construct
();
22
$this
->
machine
=
$machine
;
23
}
24
25
// ...
26
}
1
<?
php
2
3
namespace
Formativ\Deployment\Command\Machine
;
4
5
use
Formativ\Deployment\MachineInterface
;
6
use
Illuminate\Console\Command
;
7
use
Symfony\Component\Console\Input\InputArgument
;
8
use
Symfony\Component\Console\Input\InputOption
;
9
10
class
Remove
11
extends
Command
12
{
13
protected
$name
=
"deployment:machine:remove"
;
14
15
protected
$description
=
"Removes the current machine from an environment."
;
16
17
protected
$machine
;
18
19
public
function
__construct
(
MachineInterface
$machine
)
20
{
21
parent
::
__construct
();
22
$this
->
machine
=
$machine
;
23
}
24
25
// ...
26
}
Now, when we run the artisan list command; we should see all of our new package commands neatly grouped. Feel free to remove the old commands, after you’ve ported their functionality over to the new ones.
Often you’ll want to add new configuration files to the collection which ship with new Laravel applications. There’s an artisan command to help with this:
1
php artisan config:publish formativ/deployment --path=
workbench/Formativ/Deployme\
2
nt/src/config
This should copy the package configuration files into the app/config/packages/formativ/deployment directory.
Before we can publish our package, so that others can use it in their applications; we need to add a few things to the composer.json file that the workbench command created for us:
1
{
2
"name"
:
"formativ/deployment"
,
3
"description"
:
"All sorts of cool things with deployment."
,
4
"authors"
:
[
5
{
6
"name"
:
"Christopher Pitt"
,
7
"email"
:
"[email protected]"
8
}
9
],
10
"require"
:
{
11
"php"
:
">=5.4.0"
,
12
"illuminate/support"
:
"4.0.x"
13
},
14
"autoload"
:
{
15
"psr-0"
:
{
16
"Formativ\\Deployment"
:
"src/"
17
}
18
},
19
"minimum-stability"
:
"dev"
20
}
I’ve added a description, my name and email address. I also cleaned up the formatting a bit; but that’s not really a requirement. I’ve also set the minimum PHP version to 5.4.0 as we’re using things like short array syntax in our package.
To get others using your packages; you need to submit them to packagist.org. Go to http://packagist.org and sign in. If you haven’t already got an account, you should create it and link to your GitHub account.
You’ll find a big “Submit Package” button on the home page. Click it and paste the URL to your GitHub repository in the text field. Submit the form and you should be good to go.
Those of you who already know about unit testing will doubtlessly wonder where the section on testing is. I ran short on time for that in this chapter, but it will be the subject of a future chapter; which will demonstrate the benefits of dependency injection as it relates to unit testing.