Buy

Well hey! Welcome back! It's time to put our new object-oriented skills into practice. We're working on the same out of this world project: it has ships, you choose them, then they engage in epic battle!

In an editor, far far away, you'll see a simple application that runs this: index.php is the homepage and battle.php does the magic and shows the results. Last time, we created a single class called Ship, which describes all its properties - it's like a container for one ship's details:

117 lines lib/Ship.php
... lines 1 - 2
class Ship
{
private $name;
private $weaponPower = 0;
private $jediFactor = 0;
private $strength = 0;
private $underRepair;
... lines 14 - 115
}

We used this to replace these big associative arrays. Now we deal with cute Ship objects:

126 lines functions.php
... lines 1 - 4
function get_ships()
{
$ships = array();
$ship = new Ship('Jedi Starfighter');
//$ship->setName('Jedi Starfighter');
$ship->setWeaponPower(5);
$ship->setJediFactor(15);
$ship->setStrength(30);
$ships['starfighter'] = $ship;
... lines 15 - 33
return $ships;
... lines 35 - 126

Remove all the Flat Functions!

Having a huge list of flat functions in functions.php is not a good recipe for staying organized. But in just a few minutes, we'll use some new classes to give our app a whole new level of sophistication. We'll get rid of battle() first.

Look at Ship: this is a class that basically just holds data - some people call that "state", but I'll say "data" - and I'm talking about the values on a Ship object's properties. So a Ship object holds data, but it doesn't really do any work. Sure, it has some methods on it, but these just return that data, after doing some small logic at best.

Reason #1 for creating a class is this: we need some organized unit to hold data.

But there's a second big reason to create a class: because you need to do some work. For example, in functions.php, the battle() function does work: we give it 2 Ships, it does some calculations, executes logic to see how different strengths affect each other and ultimately returns the result of that work.

And we're all familiar with creating functions like this. And here's the secret for OO: whenever you get the urge to create a flat function like battle(), don't. Instead, create a class and with a method inside of it.

Create the BattleManager Service Class

Let's do this! Since this function is all about battling, let's create a new class called BattleManager:

59 lines lib/BattleManager.php
<?php
class BattleManager
{
... lines 5 - 57
}

Be as creative as you want with naming: I want to describe that methods in this class will do things related to battling.

Go copy and remove the flat battle() function: paste it into BattleManager. Put public in front of function. Remember, public means that code outside of this class will be able to call this:

59 lines lib/BattleManager.php
... lines 1 - 2
class BattleManager
{
/**
* Our complex fighting algorithm!
*
* @return array With keys winning_ship, losing_ship & used_jedi_powers
*/
public function battle(Ship $ship1, $ship1Quantity, Ship $ship2, $ship2Quantity)
{
... lines 12 - 51
return array(
'winning_ship' => $winningShip,
'losing_ship' => $losingShip,
'used_jedi_powers' => $usedJediPowers,
);
}
}

And yes, you don't have to add public: functions default to public if you say nothing, but let's keep things clear!

That's all you need to change: functions work the same inside or outside of a class: they have arguments, they return stuff.

But we do need to change code where we call this function - in battle.php. So how can we call this? Well, when we want to call a method on Ship, we need to have a Ship object first. The same is true here: we need a BattleManager object first. Start with a new variable called $battleManager and create a new BattleManager object:

98 lines battle.php
... lines 1 - 28
$battleManager = new BattleManager();
... lines 30 - 98

And now say $battleManager, the arrow, then battle():

98 lines battle.php
... lines 1 - 28
$battleManager = new BattleManager();
$outcome = $battleManager->battle($ship1, $ship1Quantity, $ship2, $ship2Quantity);
... lines 31 - 98

Let's give this a shot! Refresh battle.php. Oh no! Class BattleManager not found! Epic fail!

Not really - at the top of functions.php, we have access to the Ship class because we're requiring it. Do the same for BattleManager:

73 lines functions.php
<?php
require_once __DIR__.'/lib/Ship.php';
require_once __DIR__.'/lib/BattleManager.php';
... lines 5 - 73

There is a way where you can reference classes like BattleManager without needing to worry about the require statements. It's called autoloading, it's really common, and you'll learn how to master it in a future episode. But until then: if you have a class, require it.

Go back and refresh!

Cool - totally working.

Now we have 2 reasons to create a class. First, if you have some data - like properties that describe a ship, creating a class for that is nice. You'll create a Ship object whenever you have a set of that data. In get_ships(), we create 4 Ship objects. These types of classes are sometimes called models, because they model something, like a ship.

Second, if you need to make a function that does some work: create a class and put a method in it, like BattleManager. Or, you may put multiple methods inside one class - as long as they are all thematically similar.

You'll create one of these objects - like BattleManager - just one time, before you need to call a method on it. These are sometimes called service classes, because they perform work or service. Organizing your code to use service classes can be tricky, but we'll learn all about that.

Leave a comment!