This course is archived!
This tutorial uses Symfony 3, but all the concepts around PHP 7 are still ?valid.
Void Types & Refactoring an Entire Class
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThere's one last feature with return types: void return types. setFunFact()
is a function... but it doesn't return anything. You can advertise that with : void
. This literally means that the method returns... nothing. And not surprisingly, when we refresh, it works great... because we are in fact not returning anything.
declare(strict_types = 1); | |
// ... line 3 | |
namespace AppBundle\Entity; | |
// ... lines 5 - 17 | |
class Genus | |
{ | |
// ... lines 20 - 135 | |
public function setFunFact(?string $funFact): void | |
{ | |
// ... lines 138 - 140 | |
} | |
// ... lines 142 - 224 | |
} |
But now, try to return null
. This will not work! When your return type is void
, it literally means you do not return anything. Even null is not allowed.
// ... lines 2 - 17 | |
class Genus | |
{ | |
// ... lines 20 - 135 | |
public function setFunFact(?string $funFact): void | |
{ | |
$this->funFact = $funFact; | |
return null; | |
} | |
// ... lines 142 - 224 | |
} |
The void return type isn't that important, but it's useful because (A), it documents that this method does not return anything and (B) it guarantees that we don't get crazy and accidentally return something.
Oh, but you can use the return
statement - as long as it it's just return
with nothing after.
// ... lines 2 - 17 | |
class Genus | |
{ | |
// ... lines 20 - 135 | |
public function setFunFact(?string $funFact): void | |
{ | |
$this->funFact = $funFact; | |
return; | |
} | |
// ... lines 142 - 224 | |
} |
Updating all of Genus
Great news! We now know everything about scalar type hints and return types. And we can use our new super powers to update everything in Genus
.
Let's do it! Start with getId()
, this will return an int
, but it should be nullable: there is no id
at first. For getName()
, the same thing, ?string
. For setName()
, it's up to you: this accepts a string, but I am going to allow it to be null. But if you never want that to happen, don't allow it! And of course, the method should return void
.
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 93 | |
public function getId(): ?int | |
// ... lines 95 - 98 | |
public function getName(): ?string | |
// ... lines 100 - 103 | |
public function setName(?string $name): void | |
// ... lines 105 - 222 | |
} |
For getSubFamily()
, this is easy: it will return a SubFamily
object or null. The cool part is that we don't need the PHP doc anymore! We have a return type! Amazing!
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 108 | |
public function getSubFamily(): ?SubFamily | |
// ... lines 110 - 222 | |
} |
For setSubFamily()
, mark this to return void
. Notice that the argument is SubFamily $subFamily = null
. If we want that argument to be required, we could change that to ?SubFamily
. Your call!
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 113 | |
public function setSubFamily(SubFamily $subFamily = null): void | |
// ... lines 115 - 222 | |
} |
Let's keep going! getSpeciesCount()
returns a nullable int
, though we could give that a default value of 0 if we want, and remove the question mark. setSpeciesCount()
accepts a nullable int
and returns void
. Again, the nullable part is up to you.
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 118 | |
public function getSpeciesCount(): ?int | |
// ... lines 120 - 123 | |
public function setSpeciesCount(?int $speciesCount): void | |
// ... lines 125 - 222 | |
} |
For getUpdatedAt()
, set its return type to a nullable DateTimeInterface
, because this starts as null
. But notice... I'm returning a DateTime
object... so why make the return-type DateTimeInterface
? Well, it's up to you. With this type-hint, I could update my code later to return a DateTimeImmutable
object. But more importantly, the return type is what you're "advertising" to outsiders. Choose whatever makes the most sense.
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 140 | |
public function getUpdatedAt(): \DateTimeInterface | |
// ... lines 142 - 222 | |
} |
Ok, let's get this done! setIsPublished
takes a bool
that is not nullable, and it returns void
. getIsPublished
will definitely return a bool
- we initialized it to a bool
when we defined the property.
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 145 | |
public function setIsPublished(bool $isPublished): void | |
// ... lines 147 - 150 | |
public function getIsPublished(): bool | |
// ... lines 152 - 222 | |
} |
For getNotes()
, return a Collection
and then update the PHPDoc to match. There are two interesting things happening. First, ArrayCollection
implements this Collection
interface, so using the interface is a bit more flexible. Normally, that's just a choice you can make: set your return type to the class you know you're returning... or use the more flexible interface. But actually, for Doctrine collections, you must use Collection
. Depending on the situation, this property might be an ArrayCollection
or a PersistentCollection
... both of which implement the Collection
interface. In other words, the only guarantee we can make is that this returns the Collection
interface.
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 155 | |
/** | |
* @return Collection|GenusNote[] | |
*/ | |
public function getNotes(): Collection | |
// ... lines 160 - 222 | |
} |
Second, this returns a collection of GenusNote
objects. In other words, if you call getNotes()
and then loop over the results, each item will be a GenusNote
. But there's no way to denote that with return types. That's why we're keeping the |GenusNote[]
. That helps my editor when looping.
getFirstDiscoveredAt()
returns a nullable DateTimeInterface
and setFirstDiscoveredAt()
returns void
. getSlug()
will be a nullable string, setSlug()
will accept a nullable string argument and return void
. addGenusScientists()
will return void and removeGenusScientist()
the same. For getGenusScientists()
, like before, I'll set the return type to Collection
and update the PHP doc. Do the same for getExpertScientists()
: return Collection
. This PHPDoc is already correct... but I'll shorten it.
// ... lines 2 - 18 | |
class Genus | |
{ | |
// ... lines 21 - 163 | |
public function getFirstDiscoveredAt(): ?\DateTimeInterface | |
// ... lines 165 - 168 | |
public function setFirstDiscoveredAt(\DateTime $firstDiscoveredAt = null): void | |
// ... lines 170 - 173 | |
public function getSlug(): ?string | |
// ... lines 175 - 178 | |
public function setSlug(?string $slug): void | |
// ... lines 180 - 183 | |
public function addGenusScientist(GenusScientist $genusScientist): void | |
// ... lines 185 - 194 | |
public function removeGenusScientist(GenusScientist $genusScientist): void | |
// ... lines 196 - 205 | |
/** | |
* @return Collection|GenusScientist[] | |
*/ | |
public function getGenusScientists(): Collection | |
// ... lines 210 - 213 | |
/** | |
* @return \Doctrine\Common\Collections\Collection|GenusScientist[] | |
*/ | |
public function getExpertScientists(): Collection | |
// ... lines 218 - 222 | |
} |
Phew! That makes our class a lot tighter: it's now more readable and more difficult to make mistakes. But it also took some work! The cool thing is that you have the power to add return types and type-hint arguments wherever you want. But you don't need to do it everywhere.
After all those changes... did we break anything? Find your browser and go to /genus/new
. This is a "dummy" URL that creates and saves a Genus
behind the scenes. So apparently, that still works! Click the Genus to go to its show page. Then, login using weaverryan+1@gmail.com
and password iliketurtles
. Once you do that, click to edit the genus.
Let's see... change the species to 5000, keep the fun fact empty and change the name. Hit enter!
Yay! Everything still works! And our Genus
class is awesome!
@ 4:00 when you set Collection, it went WAY too fast, that you actually used phpStorm Auto-complete to add use statement for Doctrine\Common\Collection.
Am I the only one, that thinks these tutoials sometimes write too fast? trying to keep up, I have to keep pausing video
+ Last step for Login, does not bring you to genus overview page.