Login to bookmark this video
Buy Access to Course
13.

Bye Bye AppBundle

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

If you want to stop now, you can! Your old code lives in src/AppBundle, but it works! Over time, you can slowly migrate it directly into src/.

Or! We can keep going: take this final challenge head-on and move all our files at once! If you're not using PhpStorm... this will be a nightmare. Yep, this is one of those rare times when you really need to use it.

Moving your Files

Open AppBundle.php. Then, right click on the AppBundle namespace and go to Refactor -> Move. The new namespace will be App. And below... yea! The target destination should be src/.

This says: change all AppBundle namespaces to App and move things into the src/ directory. Try it! On the big summary, click OK!

10 lines | src/AppBundle.php
// ... lines 1 - 2
namespace App;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
}

In addition to changing the namespace at the top of each file, PhpStorm is also searching for references to the namespaces and changing those too. Will it be perfect? Of course not! But that last pieces are pretty easy.

Woh! Yes! Everything is directly in src/. AppBundle is now empty, except for a fixtures.yml file. We're going to replace that file soon anyways.

Delete AppBundle! That felt amazing!

Refactoring tests/

Let's do the same thing for the tests/ directory... even though we only have one file. Open DefaultControllerTest.php and Refactor -> Move its namespace. In Flex, the namespace should start with App\Tests. Then, press F2 to change the directory to tests/Controller.

19 lines | tests/Controller/DefaultControllerTest.php
// ... lines 1 - 2
namespace App\Tests\Controller;
// ... lines 4 - 6
class DefaultControllerTest extends WebTestCase
{
// ... lines 9 - 17
}

Ok, Refactor! Nice! Now delete that AppBundle.

Cleaning up AppBundle

With those directories gone, open composer.json and find the autoload section. Remove both AppBundle parts.

So... will it work? Probably not - but let's try! Refresh! Ah!

The file ../src/AppBundle does not exist in config/services.yaml

Ah, that makes sense. Open that file: we're still trying to import services from the old directory. Delete those two sections. And, even though it doesn't matter, remove AppBundle from the exclude above.

In routes.yaml, we also have an import. Remove it! Why? Annotations are already being loaded from src/Controller. And now, that's where our controllers live!

Oh, and change AppBundle to App for the homepage route - I can now even Command+Click into that class. Love it!

5 lines | config/routes.yaml
homepage:
path: /
defaults:
_controller: App\Controller\MainController::homepageAction

Back in services.yaml, we still have a lot of AppBundle classes in here: PhpStorm is not smart enough to refactor YAML strings. But, the fix is easy: Find all AppBundle and replace with App.

56 lines | config/services.yaml
// ... lines 1 - 17
App\:
// ... lines 19 - 23
App\Controller\:
// ... lines 25 - 33
App\Service\MarkdownTransformer:
// ... lines 35 - 37
App\Doctrine\HashPasswordListener:
// ... lines 39 - 40
App\Form\TypeExtension\HelpFormExtension:
// ... lines 42 - 44
App\Service\MessageManager:
// ... lines 46 - 49
App\EventSubscriber\AddNiceHeaderEventSubscriber:
// ... lines 51 - 56

Done! There is one last thing we need to undo: in config/packages/doctrine.yaml. Remove the AppBundle mapping we added.

So, what other AppBundle things haven't been updated yet? It's pretty easy to find out. At your terminal, run:

git grep AppBundle

Hey! Not too bad. And most of these are the same: calls to getRepository(). Start in security.yaml and do the same find and replace. You could do this for your entire project, but I'll play it safe.

41 lines | config/packages/security.yaml
// ... lines 1 - 2
security:
encoders:
App\Entity\User: bcrypt
// ... lines 6 - 10
providers:
our_users:
entity: { class: App\Entity\User, property: email }
// ... line 14
firewalls:
// ... lines 16 - 20
main:
// ... line 22
guard:
authenticators:
- App\Security\LoginFormAuthenticator
// ... lines 26 - 41

Now, completely delete the AppBundle.php file: we're already not using that. Next is GenusAdminController. Open that class. But instead of replacing everything, which would work, search for AppBundle. Ah! It's a getRepository() call!

Our project has a lot of these... and... well... if you're lazy, there's a secret way to fix it! Just change the alias in doctrine.yaml from App to AppBundle. Cool... but let's do it the right way! Use Genus::class.

// ... lines 1 - 16
class GenusAdminController extends Controller
{
// ... lines 19 - 21
public function indexAction()
{
$genuses = $this->getDoctrine()
->getRepository(Genus::class)
// ... lines 26 - 30
}
// ... lines 32 - 96
}

We have a few more in GenusController. Use SubFamily::class, User::class, Genus::class, GenusNote::class and GenusScientist::class.

144 lines | src/Controller/GenusController.php
// ... lines 1 - 17
class GenusController extends Controller
{
// ... lines 20 - 22
public function newAction()
{
// ... lines 25 - 26
$subFamily = $em->getRepository(SubFamily::class)
// ... lines 28 - 42
$user = $em->getRepository(User::class)
// ... lines 44 - 60
}
// ... lines 62 - 65
public function listAction()
{
// ... lines 68 - 69
$genuses = $em->getRepository(Genus::class)
// ... lines 71 - 75
}
// ... lines 77 - 80
public function showAction(Genus $genus, MarkdownTransformer $markdownTransformer, LoggerInterface $logger)
{
// ... lines 83 - 88
$recentNotes = $em->getRepository(GenusNote::class)
// ... lines 90 - 96
}
// ... lines 98 - 127
public function removeGenusScientistAction($genusId, $userId)
{
// ... lines 130 - 131
$genusScientist = $em->getRepository(GenusScientist::class)
// ... lines 133 - 141
}
}

Ok, back to the list! Ah, a few entities still have AppBundle. Start with Genus. The repositoryClass, of course! Change AppBundle to App. There's another reference down below on a relationship. Since all the entities live in the same directory, this can be shortened to just SubFamily.

223 lines | src/Entity/Genus.php
// ... lines 1 - 12
/**
* @ORM\Entity(repositoryClass="App\Repository\GenusRepository")
// ... line 15
*/
class Genus
{
// ... lines 19 - 37
/**
// ... line 39
* @ORM\ManyToOne(targetEntity="App\Entity\SubFamily")
// ... line 41
*/
private $subFamily;
// ... lines 44 - 221
}

Make the same change in GenusNote, SubFamily and User.

101 lines | src/Entity/GenusNote.php
// ... lines 1 - 6
/**
* @ORM\Entity(repositoryClass="App\Repository\GenusNoteRepository")
// ... line 9
*/
class GenusNote
// ... lines 12 - 101
45 lines | src/Entity/SubFamily.php
// ... lines 1 - 6
/**
* @ORM\Entity(repositoryClass="App\Repository\SubFamilyRepository")
// ... line 9
*/
class SubFamily
// ... lines 12 - 45
223 lines | src/Entity/User.php
// ... lines 1 - 11
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
// ... lines 14 - 15
*/
class User implements UserInterface
// ... lines 18 - 223

Almost done! Next is GenusFormType: open that and change the data_class to Genus::class.

109 lines | src/Form/GenusFormType.php
// ... lines 1 - 21
class GenusFormType extends AbstractType
{
// ... lines 24 - 60
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Genus::class
]);
}
// ... lines 67 - 107
}

Then, finally, LoginFormAuthenticator. Update AppBundle:User to User::class.

90 lines | src/Security/LoginFormAuthenticator.php
// ... lines 1 - 20
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
// ... lines 23 - 56
public function getUser($credentials, UserProviderInterface $userProvider)
{
// ... lines 59 - 60
return $this->em->getRepository(User::class)
// ... line 62
}
// ... lines 64 - 88
}

Phew! Search for AppBundle again:

git grep AppBundle

They're gone! So... ahh... let's try it! Refresh! Woh! An "Incomplete Class" error? Fix it by manually going to /logout. What was that? Well, because we changed the User class, the User object in the session couldn't be deserialized. On production, your users shouldn't get an error, but they will likely be logged out when you first deploy.

Go back to /admin/genus, then login with weaverryan+1@gmail.com, password iliketurtles. Guys, we're done! We have a Symfony 4 app, built on the Flex directory structure, and with no references to AppBundle! And it was all done in a safe, gradual way.

To celebrate, I've added one last video with a few reasons to be thrilled that you've made it this far.