If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Here's the next challenge, sometimes I want my ships to be broken. So when
a ship comes in it might be able to fight or it might be under repair. This
is a property on a Ship
, a Ship
could be under repair or not. So I'll
add this as a new private property that has a boolean which can be true or
false:
... lines 1 - 2 | |
class Ship | |
{ | |
... lines 5 - 12 | |
private $underRepair; | |
... lines 14 - 108 | |
} |
The challenge is that whenever we create our objects inside of functions.php
,
I want to randomly set a Ship
to be under repair or not under repair, and
I want it to happen automatically. So just by creating a Ship
, I want it
to internally figure out if it is under repair or not.
This is where the idea of a constructor comes in. Whenever you create an
object, you can actually hook into that process and say: "Hey whenever a
Ship
is created, call this function so I can set some stuff up." The way
you do this is by creating a very special public function inside of your
class called __construct()
:
... lines 1 - 2 | |
class Ship | |
{ | |
... lines 5 - 14 | |
public function __construct() | |
{ | |
echo 'Automatically called!'; | |
} | |
... lines 19 - 108 | |
} |
The magic here is that name: it must be __construct
. And just by having
this it should be called everytime we say new Ship()
.
Let's try it, refresh! And that's it: it's called four times, once for each
of our ships. And it's called right when you say new Ship()
, so if I throw
in a die()
statement right after creating a Ship
, we're still going to
see one of those called.
Now we have a really powerful way to set up our data. Internally we can determine
whether or not the Ship
is under repair. We'll use $this- underRepair = mt_rand(1, 100) < 30;
:
... lines 1 - 2 | |
class Ship | |
{ | |
... lines 5 - 14 | |
public function __construct() | |
{ | |
// randomly put this ship under repair | |
$this->underRepair = mt_rand(1, 100) < 30; | |
} | |
... lines 20 - 114 | |
} |
This gives each ship a 30% chance of being broken...maybe a wing fell off!
To see this in action, let's cheat real quick and var_dump
the $ships
array. When we refresh we can see the first two ships are ready for action
but the third one isn't. Refresh again and they're all ready to fly. And
a third time shows that the first two are busted and the other two are
battle worthy. So that's working already!
Next, let's go into index.php
and up top we have our table information.
Let's include status which will tell us if our ship is under repair or not:
... lines 2 - 56 | |
<thead> | |
<tr> | |
<th>Ship</th> | |
... lines 60 - 62 | |
<th>Status</th> | |
</tr> | |
</thead> | |
... lines 66 - 118 |
Now so far, we don't have a way to access that new property. It's private
and we don't have a getter or a setter and you don't necessarily need to
create these. In fact, we don't want a setter: it's being set automatically
inside of the class itself. But I do want to figure out if this Ship
is
functional or not. So what I'll do is create a new public
function and I'll call it isFunctional()
:
... lines 1 - 2 | |
class Ship | |
{ | |
... lines 5 - 20 | |
public function isFunctional() | |
{ | |
return !$this->underRepair; | |
} | |
... lines 25 - 114 | |
} |
This will be the opposite of the underRepair
value. If it is underRepair
,
then it is not functional and if it is functional then it is not underRepair
.
For the outsider whose going to be calling this function, they don't care
what we're doing internally to figure that out.
Let's go back to index.php
and create a nice if statement. If $ship
is
functional, else
, and we'll put some adorable icons:
... lines 1 - 66 | |
<?php foreach ($ships as $ship): ?> | |
<tr> | |
... lines 69 - 72 | |
<td> | |
<?php if ($ship->isFunctional()): ?> | |
<i class="fa fa-sun-o"></i> | |
<?php else: ?> | |
<i class="fa fa-cloud"></i> | |
<?php endif; ?> | |
</td> | |
</tr> | |
<?php endforeach; ?> | |
... lines 82 - 118 |
A sunshine for functional and a sad cloud for not functional.
Refresh and try it out, four sunshines and one cloud. Awesome!
Now it's really easy to do the next step. If a Ship
is under repair, I
don't want it to show up in this select menu. It's easy because we can just
call isFunctional
and it will take care of all the internal stuff for us.
Down here we only want to print this out if the ship is in working order.
And the same thing down here:
... lines 1 - 91 | |
<?php foreach ($ships as $key => $ship): ?> | |
<?php if ($ship->isFunctional()): ?> | |
<option value="<?php echo $key; ?>"><?php echo $ship->getNameAndSpecs(); ?></option> | |
<?php endif; ?> | |
<?php endforeach; ?> | |
... lines 97 - 103 | |
<?php foreach ($ships as $key => $ship): ?> | |
<?php if ($ship->isFunctional()): ?> | |
<option value="<?php echo $key; ?>"><?php echo $ship->getNameAndSpecs(); ?></option> | |
<?php endif; ?> | |
<?php endforeach; ?> | |
... lines 109 - 118 |
Cool! Refresh, all sunshines. Refresh again -- there's a cloud. It looks like we're missing the Cloakshape Fighter due to repairs. And when you check the list, it isn't there! Perfect!
The __construct()
function is something you are going to see a lot but
it's a really easy idea. It just says if you have a function called __construct()
,
then it's automatically going to be called when you instantiate your object.
There is one other thing you can do: like most functions, it can have an
argument. Let's put a $name
argument here:
... lines 1 - 14 | |
public function __construct($name) | |
{ | |
... line 17 | |
// randomly put this ship under repair | |
$this->underRepair = mt_rand(1, 100) < 30; | |
} | |
... lines 21 - 117 |
I'm not going to use it yet because I'm going to show you what happens when we do that.
Go back to functions.php
. You can see that my editor is angry because it
says required parameter $name
missing:
... lines 1 - 8 | |
$ship = new Ship(); | |
... lines 10 - 129 |
So, you notice whenever we create a new Ship
object, it's always Ship()
,
but you never pass anything in there. When you create an object, the stuff
that goes in between the parenthesis are arguments that are passed to your
__construct()
function, if you have one. Because we have a $name
argument
here, now we need to pass a name there, just like that:
... lines 1 - 8 | |
$ship = new Ship('Jedi Starfighter'); | |
... lines 10 - 126 |
Now you can see that it is happy.
And what we can do inside of Ship.php
is say, ok whatever $name
they
pass in, let's just set that to the name
property:
... lines 1 - 14 | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
// randomly put this ship under repair | |
$this->underRepair = mt_rand(1, 100) < 30; | |
} | |
... lines 21 - 117 |
In functions.php
, we don't have to call setName()
anymore: we're passing
it into the constructor and the name is being set that way. Let's update
the other ones as well:
... lines 1 - 8 | |
$ship = new Ship('Jedi Starfighter'); | |
//$ship->setName('Jedi Starfighter'); | |
... lines 11 - 15 | |
$ship2 = new Ship('CloakShape Fighter'); | |
... lines 17 - 21 | |
$ship3 = new Ship('Super Star Destroyer'); | |
... lines 23 - 27 | |
$ship4 = new Ship('RZ-1 A-wing interceptor'); | |
... lines 29 - 126 |
And we're good to go!
So, why would you do this? Why would you add a $name
argument to the Ship's
constructor and force it to be passed in versus the setter? It's really up
to you. In our case, it doesn't make sense to have a Ship
without a name.
And before, that would have been possible had we just instantiated a new
Ship
and forgotten to call setName()
. Then we would have been running
around with a Ship
object that had absolutely no name. How embarrassing.
Sometimes, when you have required information, you might choose to set them
up as arguments to your constructor. It says "Hey, when you create a Ship
,
you have to pass me a name." We're not forcing the user to pass a weaponPower
,
jediFactor
or strength
, because those all have a nice default value of
0. So it makes sense not to force those, but we do force the name.
When you back up I just want you to realize that the __construct
function
is just like any other function. But if you give it that special name, it
is automatically called and the arguments are passed to it.
And guess what! You just learned the fundamentals of object-oriented programming. Classes, check! Objects, super check! Methods, privacy, type-hinting, constructor and other stuff - all old news now. And there's even more great stuff to learn, like service classes, dependency injection, inheritance and interfaces. These will make you even more dangerous, and will also help you understand the outside libraries you use everyday. So keep going, and join us for episode 2.
Seeya next time!
Ah, that's very nice! I agree - it's a really solid idea, especially as we keep developing the OO track. We have a tracker for course ideas - I've just added your comment to it.
Thanks!
Hey Krzysztof! Do you mean what has been updated on the tutorial - because you're seeing the new "updated" label on some tutorials?
We don't have any specific details on *what's* been updated on a tutorial yet - but I've added that to our issue tracker (I'd like to show a log of what was updated and when). In this case, we made some minor changes to the course description - so nothing too interesting :).
Hey György C.!
Fair question! Check out the battle.php
file - you'll see it there. Basically, if a form is POST'ed to battle.php and the form is missing some data, we redirect back to /index.php?error= with the error code. We built this out in our PHP intro course (but didn't explain it here). Let me know if I an offer more explanation.
Cheers!
Hey weaverryan !
I see the code in battle.php, thanks.
Do you have a course in that you build up this project from the ground up to the point where the oop part begins?
I've only found the projet for "pet's listing website".
Anyway, I really appriciate all the work you are doing on 'symphonycasts'.
Best!
Hey György C. !
Yea know what? I was totally wrong (bad memory!). This is the *first* course where we use this code: so there is no course where we build this app from scratch. Sorry about that! The PHP track - https://symfonycasts.com/tr... - builds a different project, but that's where we cover the concepts in the "starting" code for this course.
If you have any questions about the code along the way (like you already asked), we'd be happy to do our best to answer them :).
Cheers!
Hi there. Is it ever likely that the Ship properties are going to be 0? When would it be more beneficial to use setters for these properties rather than via the constructor? It would seem you can do both so there must be some underlying principal as to when to choose either approach?
Thank you :)
Hey ThingyBob,
Usually, if some object properties are required to be set - you can make it setting in the constructor. Because, if you do not pass required arguments into constructor - the object won't be created and you'll get a fatal error. But if some properties are optional during instantiating the object - you may use setters to be able to set them later if necessary. Basically, you can think of it this way. So, if you want sure that some fields are set during object creating - do it in its constructor. of other properties that you need to set after object was created, e.g. during its modification - sue setters.
And we set some properties to 0 just to give some reasonable defaults. We just want to make sure that those properties are "numbers" not "null".
I hope it makes sense to you.
Cheers!
Hi there,
I would like to know how you get these nice formatted var_dump output.
When I use var_dump($somevariable); die; and view it in firefox or chrome I get unstyled stuff in one long single line.
Hey harm2o,
Good questions! :) Yeah, it's a special PHP extension called Xdebug - you can install it and your output will be similar the same as ours. Or, you can look into a Symfony component called VarDumper: https://symfony.com/doc/cur... - but you can use it outside of Symfony projects as well, it just has a nice integration with Symfony web developer toolbar.
Cheers!
Hey Harm2o,
Awesome! It has a lot of good things.. but take a look at the Symfony's VarDumper as well, it really nice and helps even better in Symfony apps ;)
Cheers!
I wonder if it would be possible that some tutorials around OOP design patterns could be considered (angled at PHP)? There is lots on the web, but most of it is written in hard to understand language with very poor examples. It would be awesome to have something written in KNP-speak that everyone can understand. Just an idea. :)