This tutorial is built using Symfony 4, but most of the concepts apply fine to Symfony 5!
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"doctrine/annotations": "^1.8", // v1.8.0
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knpuniversity/lorem-ipsum-bundle": "*@dev", // dev-master
"nexylan/slack-bundle": "^2.0,<2.2", // v2.0.1
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.1.6
"symfony/asset": "^4.0", // v4.0.6
"symfony/console": "^4.0", // v4.0.6
"symfony/flex": "^1.0", // v1.21.6
"symfony/framework-bundle": "^4.0", // v4.0.6
"symfony/lts": "^4@dev", // dev-master
"symfony/twig-bundle": "^4.0", // v4.0.6
"symfony/web-server-bundle": "^4.0", // v4.0.6
"symfony/yaml": "^4.0", // v4.0.6
"weaverryan_test/lorem-ipsum-bundle": "^1.0" // v1.0.0
},
"require-dev": {
"easycorp/easy-log-handler": "^1.0.2", // v1.0.4
"sensiolabs/security-checker": "^4.1", // v4.1.8
"symfony/debug-bundle": "^3.3|^4.0", // v4.0.6
"symfony/dotenv": "^4.0", // v4.0.6
"symfony/maker-bundle": "^1.0", // v1.1.1
"symfony/monolog-bundle": "^3.0", // v3.2.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.3.3
"symfony/stopwatch": "^3.3|^4.0", // v4.0.6
"symfony/var-dumper": "^3.3|^4.0", // v4.0.6
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.0.6
}
}
12 Comments
I have a question i have not been able to find an answer to for a long time: how can I make a relation with a bundles entity?
consider: a one-to-many relation (from my app to the bundle) specifically, this would require that the bundles entity contains the foreign key and therefore editing the bundle code. However, we shouldn't edit the bundle code as other apps that use the bundle will not have this particualr, app specific relation. So editing the bundle code for this apps needs is not suitable.
I've tried: a lot of things, in particular, I've tried extending the bundle class to add the relation. The problem is that you also have other entities within the bundle that have a relation to the base class. These entities will continue to refer to the base class and not be updated to refer to the (new) extended class - resulting in errors.
I've also tried a compositional approach with object reflection: when I build a class to contain the bundle entity and route the method calls to the contained bundle entity (through reflection I think). I aimed to "expand" the api of the "extending" entity with relation method calls, but I couldnt get this to work either.
Any thoughts?
Also I realized this problem doesn't just apply to people looking to extend their own bundles but also 3rd party bundles they don't control and cant edit. So how can people extend entities in 3rd party bundles?
Or is the solution an in-elegant one of copying the git repo (to "own" the code) and directly inserting the relations into the entity. This would work but it creates problems.
Extending a bundle entity would be a common need I would expect. So I'm suprised I havent been able to find an answer.
Would really love to find how to solve this!
Hey Cameron,
Complex question :) Well, I would recommend you to provide models instead of entities in your bundles, and users will have to extend those models in their entities to bring some boilerplate code from the bundles into their own projects. The benefit would be to have all the entities in your project, i.e. inside of your
src/Entity/where you have full control of everything and can create any additional relations you want. You may also want to provide in your bundles some interfaces/abstract classes/traits that uses will have to implement/extend/use in order the bundle could interact with the project's code.Maybe take a look at the FOSUserBundle implementation: https://github.com/FriendsOfSymfony/FOSUserBundle or other popular bundles that have to work with entities to get more ideas. Otherwise, probably using some configuration options that users can change via the configuration files that will dynamically will set the correct mapping for the entities, IIRC it should be possible, but I have never tried to do it and also it might be kinda confusing for users. IMO, asking users to implement an interface or extend a base class or use a trait would be more straightforward for this.
I hope this helps! :)
Cheers!
Thanks victor. By “model instead of entities”, it sounds like you could simply use @mappedsuperclass to achieve the same thing?
I found another possible alternative; the relations can be remapped at runtime via a doctrine listener. allowing for all the other entities to refer to the newly extended class. Although I think the methods will need to use interfaces in their signature to handle the extended class.
I’ll see if I can put together a sample repo.
This seems like an issue that a lot of devs looking to write modular, reusable code will run into. It would be great to see a video on how to do this within symfony.
Hey Cameron,
Well, mapped superclass is a bit more advanced and complex, I just mean about providing some code that you would extend with your entities in your src/ dir, something simpler.
Yeah, nice catch.. I believe that's what you need if you make it complex, otherwise just provide some base classes you can extend.
It might be a little know fact... but not sure that many devs may need that kind of complexity, but if you're trying to create really complex bundle that provides some entities - yeah, that the way I believe.
Cheers!
Thanks, it's worth me explaining my thoughts as I think it's quite a common need for developers and something I think should be included in the bundle training as not knowing this is a show stopper. I've personally run into this requirement multiple times as a dev and my projects have not been that exotic.
Maybe you can suggest something I'm missing here, but being able to extend (specifically: to add a relation to a third party bundle entity or my own bundle - without editing the code) is common requirement - and one that's proven quite frustrating to find an answer to.
Simply put: devs cannot write modular & reusable code without the ability to extend (add relations to) non-editable entites.
If I write code that I could re-use in another library and want to create a re-usable bundle: I first need to decouple / generalize the bundle code from my app's specific use (practically, this invovles removing relation code that exists between my app and the re-usable bundle), so this bundle code can be used in another app. These other app's are likely to need to add their own relations to between their app's entities and the bundles entities also.
This leads to the problem I'm having: it's not possible to add a many-to-one relation to a bundle's entity without editing the code (e.g. cloning the repo and editing it directly). You can't simply extend a bundle class to add a relation, create a "containing class" to do composition using reflection (to mimic the entities interface).
This simply means: devs can't create a re-usable bundle where there's a requirement to have a relation with a bundle entity.
More over, it's a nightmare trying to find a solution - many lost hours/days trying to design and implement a solution. Symfonycasts could cover this in their training to prevent others falling down the rabbit hole of this problem. Also: cloning the repo and adding in my app specific relations is a poor solution for this problem.
I did find the solution:
doctrine.orm.resolve_target_entities
it's pretty cool and makes sense that this feature comes from doctrine.
Hey Cameron,
That is a perfect catch, I personally didn't know about that feature because I never needed something like this. Thanks for sharing it with others, and here's a nice example of how to use it: https://symfony.com/doc/current/doctrine/resolve_target_entity.html
Cheers!
nice article - good explaination.
When i run ./vendor/bin/simple-phpunit it raises an exception
Fatal error: Uncaught RuntimeException: Could not find https://github.com/sebastianbergmann/phpunit/archive/6.5.zip in C:\www\sf_bundles_scrach\vendor\symfony\p<br />hpunit-bridge\bin\simple-phpunit:65<br />Stack trace:<br />#0 {main}<br /> thrown in C:\www\sf_bundles_scrach\vendor\symfony\phpunit-bridge\bin\simple-phpunit on line 65<br />It seem that the corresponding branches have been deleted from github.
You can then install manually <b>symfony/phpunit-bridge</b> to resolve.
Hey Helmi,
Sorry for the very long reply! Yeah, it looks like PHPUnit 6.5 support was dropped, you need to upgrade "symfony/phpunit-bridge" package, i.e. call "composer update symfony/phpunit-bridge" to be able to call "bin/phpunit" or "./vendor/bin/simple-phpunit". Thank you for reporting this! I upgraded the tutorial's code so it should work out of the box now.
Cheers!
Hello, could you tell me, from what course this PHPUnit test exists here?
Hey Dmitriy,
This test was added in *this* course, but we did it behind the scene because we don't want to cover testing in this course. If you're interested in testing with PHPUnit, check out our PHPUnit course: https://symfonycasts.com/sc...
Cheers!
it's clear
"Houston: no signs of life"
Start the conversation!