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 SubscribeOkay enough with type hints and return types! PHP 7.1 added another cool feature for class constants: we can finally make them private!
In GenusController
find, getNotesAction()
. This is used by an AJAX call to load notes that appear at the bottom of the genus show page. For example, go to /genus
and click on one of them. Bam! At the bottom, an AJAX call loads a list of notes, complete with an avatar. That comes from the avatarUri
field that's returned.
Look at the /images/
part: that looks funny to me. All of our images are stored in that directory, and that's fine. But I hate having random strings like this in my code. This is a perfect place to use... drum roll... a constant!
Open GenusNote
, which is the object we're rendering on this page. Add a new constant: const AVATAR_FILE_PREFIX = '/images';
. Then, in the controller, use this: GenusNote::AVATAR_FILE_PREFIX
.
... line 2 | |
namespace AppBundle\Entity; | |
... lines 4 - 10 | |
class GenusNote | |
{ | |
const AVATAR_FILE_PREFIX = '/images'; | |
... lines 14 - 101 | |
} |
... lines 1 - 15 | |
class GenusController extends Controller | |
{ | |
... lines 18 - 118 | |
public function getNotesAction(Genus $genus) | |
{ | |
... lines 121 - 122 | |
foreach ($genus->getNotes() as $note) { | |
$notes[] = [ | |
... lines 125 - 126 | |
'avatarUri' => GenusNote::AVATAR_FILE_PREFIX.'/'.$note->getUserAvatarFilename(), | |
... lines 128 - 129 | |
]; | |
} | |
... lines 132 - 137 | |
} | |
... lines 139 - 158 | |
} |
So far, this is all stuff we've seen before. And when we refresh... yep! Everything still loads.
This is an improvement... but it would be even better if I could call a method on GenusNote
to get the complete avatarUri
string, instead of calculating it here in my controller. In other words, I don't want anyone to use our constant anymore: we're going to add a public method instead.
In PHP 7.1, we can say private const
.
... lines 1 - 10 | |
class GenusNote | |
{ | |
private const AVATAR_FILE_PREFIX = '/images'; | |
... lines 14 - 101 | |
} |
Now, accessing that constant from outside this class is illegal! That means, if we refresh, our AJAX calls are failing! On the web debug toolbar, yep! You can see the 500 errors! If I open the profiler and click "Exception", we see
Cannot access private const from the controller
Awesome! So now that I can't use the constant anymore, I'll be looking for a public function to use instead. In GenusNote
, add a public function getUserAvatarUri()
. Hey! We're PHP 7 pros now, so add a string
return type.
... lines 1 - 10 | |
class GenusNote | |
{ | |
... lines 13 - 64 | |
public function getUserAvatarUri(): string | |
{ | |
... lines 67 - 73 | |
} | |
... lines 75 - 114 | |
} |
Before we add the logic, let's make things fancier. Suppose that sometimes there is not a userAvatarFilename
value for a note. If that's true, let's show a default avatar image.
Back at the top, add another private const BLANK_AVATAR_FILENAME = 'blank.jpg'
. We'll pretend that we have a blank.jpg
file that should be used when there's no avatar.
... lines 1 - 10 | |
class GenusNote | |
{ | |
... lines 13 - 14 | |
private const BLANK_AVATAR_FILENAME = 'blank.jpg'; | |
... lines 16 - 114 | |
} |
Back in the new method, add $filename = $this->getUserAvatarFilename();
. And, if (!$filename)
, then $filename = self::BLANK_AVATAR_FILENAME
... because we can access the private constant from inside the class. Finish the method with return self::AVATAR_FILE_PREFIX.'/'.$filename;
.
... lines 1 - 10 | |
class GenusNote | |
{ | |
... lines 13 - 64 | |
public function getUserAvatarUri(): string | |
{ | |
$filename = $this->getUserAvatarFilename(); | |
if (!$filename) { | |
$filename = self::BLANK_AVATAR_FILENAME; | |
} | |
return self::AVATAR_FILE_PREFIX.'/'.$filename; | |
} | |
... lines 75 - 114 | |
} |
Nice! Back in the controller, we're still accessing the private constant, which is super obvious. That'll push me to use the public function getUserAvatarUri()
.
... lines 1 - 15 | |
class GenusController extends Controller | |
{ | |
... lines 18 - 118 | |
public function getNotesAction(Genus $genus) | |
{ | |
... lines 121 - 122 | |
foreach ($genus->getNotes() as $note) { | |
$notes[] = [ | |
... lines 125 - 126 | |
'avatarUri' => $note->getUserAvatarUri(), | |
... lines 128 - 129 | |
]; | |
} | |
... lines 132 - 137 | |
} | |
... lines 139 - 158 | |
} |
Refresh one more time! Love it!
Hahaha :). I couldn't agree more! This is one of the easiest wins out there!!!!
And I like what you did there. Hmm... if you bring a bunch of people to the site to learn more about this, of course! ;)
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.3.*", // v3.3.18
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
"symfony/swiftmailer-bundle": "^2.3", // v2.5.4
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.3.0
"sensio/distribution-bundle": "^5.0", // v5.0.19
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.25
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"knplabs/knp-markdown-bundle": "^1.4", // 1.5.1
"doctrine/doctrine-migrations-bundle": "^1.1", // v1.2.1
"stof/doctrine-extensions-bundle": "^1.2", // v1.2.2
"composer/package-versions-deprecated": "^1.11" // 1.11.99
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.4
"symfony/phpunit-bridge": "^3.0", // v3.2.8
"nelmio/alice": "^2.1", // v2.3.1
"doctrine/doctrine-fixtures-bundle": "^2.3", // v2.4.1
"symfony/web-server-bundle": "3.3.*"
}
}
You should definitely popularize good practices like this one in your other videos!
The 'magic string' antipattern (bad practice) is so annoying... and unfortunately, from my experience - quite common.
Maybe you could make a whole series on the 'good practices'?
... and a discount for those who pass it? That would make world a better place! ;)