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 SubscribeIf 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.
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!
... 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!
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
.
... lines 1 - 2 | |
namespace App\Tests\Controller; | |
... lines 4 - 6 | |
class DefaultControllerTest extends WebTestCase | |
{ | |
... lines 9 - 17 | |
} |
Ok, Refactor! Nice! Now delete that 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!
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
.
... 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.
... 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
.
... 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
.
... 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
.
... lines 1 - 6 | |
/** | |
* @ORM\Entity(repositoryClass="App\Repository\GenusNoteRepository") | |
... line 9 | |
*/ | |
class GenusNote | |
... lines 12 - 101 |
... lines 1 - 6 | |
/** | |
* @ORM\Entity(repositoryClass="App\Repository\SubFamilyRepository") | |
... line 9 | |
*/ | |
class SubFamily | |
... lines 12 - 45 |
... 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
.
... 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
.
... 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.
Hey Robert W.
Woh that looks really massive update...
First as a probably fast and much easy solution, I'd like to mention https://github.com/rectorph... . That can really help you with upgrades(however personally I didn't tried it yet).
Or... if you wanna get lot of upgrade experience and solve everything like a pro, then take plan 2! This plan will be the best if you plan future upgrade to Symfony 5/6 and other. Also at the end of upgrade you will get more solid project!
Cheers!
AppBundle is now gone. I have found a new challenge. In my database, I have serialized a few classes. Now, when I try to retrieve those classes and try to excute a method, I get a _php_incomplete_class error because its looking for an 'AppBundle/Entity/ClassName'. It can no longer find the method in the original bundle. Is there a way to fix this?
Hey Scottie,
If I understand you right, when you have AppBundle you serialized a few entities and put them into DB, then refactor your application into bundleless structure and now tries to unserialize them? Yeah, it will throw an error. You need to write a migration command that will unserialize objects by legacy paths, convert them into new objects, serialize them again and store in the DB. But to do this, you would need to keep AppBundle legacy entities structure, so you were able to unserialize them. Then, when you finish migrating, you can safely and completely remove legacy entities.
I hope this helps.
Cheers!
Somehow my PhpStorm did not moved all the filed with a new namespace during refactoring. So, easy way for me was to move all the files from AppBundle level up. And then replace all occurrence of AppBundle with App by this script:
(on Linux)
find . -type f -exec sed -i 's/AppBundle/App/g' {} +
(on Mac - here also it could be broken by .DS_store files - just delete them, or execute this script in each directory of src one by one)
find . -type f -exec sed -i "" 's/AppBundle/App/g' {} +
Hey toporovvv
I have experienced troubles perfoming a "move refactoring" on PhpStorm as well, thanks for sharing your workaround, surely more than one will find it useful :)
Cheers!
Hi,
Once again, great tutorial, many thanks.
I have a minor issue however.
Now the namespace begins with App instead of AppBundle, so when I create my User entity, its namespace is obviously App\Entity\User.
But, using Phpstorm (current version is 2018.1) it gives me several warnings about a User class already defined.
The most annoying problem occurs with twig, who doesnt recognized my User attributes (like username or roles ...).
It recognize only the id, and when I ctrl-click on this "id", it brings me to the other App\Entity\User defined :
vendor/symfony/maker-bundle/tests/Util/fixtures/add_one_to_one_relation/User_simple_no_inverse_not_nullable.php
My Phpstorm is yet configured with Symfony plugins and the directory vendor/symfony/maker-bundle is set to "excluded".
I noticed that the Symfony plugins doesn't fully recognize symfony 4 folders, like Translations path (which point to var/cache/dev/translations, but this file seems to not exist anymore)
or the App DIrectory (app folder don't exist as well)
Is there a clean workaround for this ?
Thanks again :)
Hey Chuck norris
Maybe if you kick your computer it will get fixed! (j/k about your nickname)
First of all, the newest PhpStorm version broke some things, so you have to update your plugins, it may fix your problem. Anyways, you have to change "app, web and translations" fields (on your Symfony's plugin configuration) to use the correct values. I hope by doing so it will fix your problems :)
Cheers!
// composer.json
{
"require": {
"php": "^7.1.3",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/doctrine-bundle": "^1.6", // 1.8.1
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
"doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.1
"doctrine/orm": "^2.5", // v2.7.2
"fzaninotto/faker": "^1.7", // v1.7.1
"knplabs/knp-markdown-bundle": "^1.4", // 1.6.0
"sensio/framework-extra-bundle": "^5.0", // v5.1.3
"stof/doctrine-extensions-bundle": "dev-master", // dev-master
"symfony/asset": "^4.0", // v4.0.1
"symfony/console": "^4.0", // v4.0.1
"symfony/flex": "^1.0", // v1.9.10
"symfony/form": "^4.0", // v4.0.1
"symfony/framework-bundle": "^4.0", // v4.0.1
"symfony/lts": "^4@dev", // dev-master
"symfony/maker-bundle": "^1.0", // v1.0.2
"symfony/monolog-bundle": "^3.1", // v3.1.2
"symfony/polyfill-apcu": "^1.0", // v1.6.0
"symfony/profiler-pack": "^1.0", // v1.0.3
"symfony/security-bundle": "^4.0", // v4.0.1
"symfony/security-csrf": "^4.0",
"symfony/swiftmailer-bundle": "^3.1", // v3.1.6
"symfony/translation": "^4.0", // v4.0.1
"symfony/twig-bundle": "^4.0", // v4.0.1
"symfony/validator": "^4.0", // v4.0.1
"symfony/web-server-bundle": "^4.0", // v4.0.1
"symfony/yaml": "^4.0" // v4.0.1
},
"require-dev": {
"symfony/dotenv": "^4.0", // v4.0.1
"symfony/phpunit-bridge": "^4.0", // v4.0.1
"doctrine/doctrine-fixtures-bundle": "^3.0" // 3.0.2
}
}
I know this is an old video but I recently started working on "Symfony 4.4" application that looks more like a Symfony 3 application, hence the quotes.
This guide is exceptionally helpful on the road to getting my app working with Symfony/flex and all that goodness but I have a question about bundles.
This app has 30 Bundles created back in the day when bundles where the way to go. Also dealing with a Apps folder inside source that also has several bundles. What is a suggested plan of attack
1. Leave the "": namespace? composer is always complaining this is an issue with performance and there are some weird issues with namespaces when it comes to testing as well
2. Create an App base namespace and update everything? this scares me a little and not sure the boffins will like this idea
3. Turn the bundles into packages? Honestly don't know if this is even feasible just throwing it out there
I really want to get this app to point were it actually looks something like a Symfony 4.4 app and not some Frankenstein 3/4 version. There is also the issue with trying to move to Symfony 5, not to mention 6.
Any and all input greatly appreciated