Scroll to the bottom to view a video of this Repository maker in action. I make about 100 Repositories in less than 2 minutes.
So you need to make a lot of basic Doctrine repositories for your Symfony 5 app? I needed to do the same thing. That is why I created a Doctrine Repository maker for Symfony 5+ or any version that contains the Maker Bundle and uses the src/Repository and src/Entity directory structure.
What happened is I was not very familiar with Doctrine and EXACTLY how it wants entities to be structured.
I knew exactly how to design a database. But I didn’t feel like wasting time to learn everything about Doctrine ORM just to be able to make queries and use migrations.
Honestly I am not a big fan or ORMs because all of them are specific and take time to learn. I’ve spent over a decade with SQL thank you very much. Plus I code in so many languages, I don’t have time to learn everything about every ORM… and that is why SQL was invented.
So I have this seriously complex app I am building. It needs several hundred tables. I had well over 100 tables already created via MySQL Workbench. I love workbench because it is a nice UI that makes creating tables and making changes super fast and easy. Much faster than typing all of that mess into an Entity directly.
So what I did is I used Doctrine to reverse engineer my database and create the mapping to the Entity Annotations. That was a pain, but still faster than learning EVERYTHING about Doctrine and typing all that stuff in.
The main problems with reverse engineering with Doctrine is it doesn’t create the repositories for the entities. And in order to use Doctrine with Symfony you need a Repository for each Entity, especially if you need custom queries/methods.
Another issue I had is if you do reverse engineer your database like this and you do create the repositories, you must then go into each Entity and add the Repository imports.
In order to create the Repositories for my new Entities, I created a maker. The maker gets a list of all of the Entities and existing Repositories. It then loops through each of the Entities that does not have a Repository and asks you if you would like to create one.
If you go with the naming path of the Repository maker it can overwrite files so be careful.
I also created an Entity clone maker, which I’ll talk about and share soon. Many of my Database tables were very similar, so I created an Entity cloner which can be used with Doctrine migrations and the Repository Maker to quickly finish building my app.
The Entity cloner also loops through entities and asks if you want to clone it. Very helpful.
Then Doctrine looks in the same directory as your Entity aka the entity folder and since it can’t find it you get this error. I found that adding a simple use statement to the top of the Entity file like so works well.
You can also just add the fully qualified Repository name like this.
It is a personal choice thing. I like the use statements because I am using PHP storm and it does use statements better than it does suggesting Repository names in the Entity annotations. Actually it doesn’t suggest the fully qualified path name so that is why I personally use “use” LOL
The only problem is this technique will only work with databases that are generically designed. Meaning you can’t use most of the DB vendor specific types etc.
This means if your database uses tinyint or enum for example you should undo that before trying to reverse engineer it with the link above. And god bless you if you have used tiny ints as foreign keys. You must delete all foreign keys first, then recreate them.
In this case however Schema-Tool update will have a hard time not to request changes for this column on each call.
Anyone want to have a stab at translating what that means?
Use a backup
Don’t just backup your database. Start by dumping your current database structure, no data. Next import that into a new Schema. You can do that with one of the doctrine migration commands see this article.
Once you have the database structure imported. Open a terminal and do a quick doctrine migration diff as talked about here. Once you have the diff look over the migration to see what doctrine is wanting to change in your database schema. For me, It is easier to make those changes in something like MySQL Workbench with the added bonus that it can create an Entity Relationship diagram as part of my documentation.
Working with a backup of the data-structure lets you see what needs to be changed in your real database first. Follow the techniques in this article about reverse engineering the database.
Do the doctrine orm mapping import after you do the first diff. The purpose of the first diff is just to see the changes. You will see stuff like problems with TinyInts, Enums etc. Any vendor specific column type will generally be more trouble than it is worth.
In the process of doing the diff and creating the mapping and doing the diff again and again, you end up with lots of migrations. Delete all of the migrations except for the very last diff migration.
You do the final diff after making final changes to the entity, mapping annotations. This way Doctrine can view your entities and compare them to your actual Schema. When you run this migration it will sync your database to the Entities.
Sorry database designers you are not allowed to have these in your database design as they are mostly MySQL specific the Doctrine code does not allow them.
Convert your tinyint columns to smallint. Doctrine understands those. Sure you are using like, a little more space each row now, but you really need Doctrine. LOL You can create a custom tinyint type, but wiring it in and using it is more of a pain than it is worth unless you really need it.
And the docs are wrong on this one and need updating. MySQL no longer requires an entire table rebuild costing hours.However Enums are not very useful because NULL and an empty string can be acceptable values under normal MySql configuration. Also with enums users can choose multiple values, so Doctrine uses php array type. You can use Enums, but it is more of a pain. See the doctrine docs here. You have to create a custom type and wire it in.
Just use varchar or string as doctrine calls it. Make a check in your code (entity methods) to restrict values that are able to be accepted or make a table that holds the values. I personally use a table when there will be 10 or more values or I don’t know for certain how many there will be. If you use a table use a foreign link id to it in your main tables.
Forget your foreign key names
There is no solution for this. I have no idea why but Doctrine will absolutely rename,or suggest to rename every foreign key.
I think it tries to make sure each is unique or maybe it needs it in a specific format. I usually like to put the table name in it to be a hint in error output. But it also seems to rename keys it has previously renamed too.
Like the leg humping dog in National Lampoon… “it’s best to let it finish”
I’ll post more as I find it.
Your Entities have no repositories
It is awesome that doctrine can reverse engineer a database and even create the repositories too. What I don’t understand is why it does not link the damn repository to the Entity. When you use the Entity maker it does this. When you reverse engineer with Doctrine it only creates the Entity mappings.
So I must do this 100+ times now. I hope you have less tables in your database schema
I could add more code to the RepositoryMaker to update the Entities with the proper Repository tag. But then it is doing more than one thing. Maybe I should make a maker that updates the Entities Repository definition line to match. I wonder how long that would take compared to opening 100+ files and trying to type it properly etc.? Looks like I need to create a new maker.
To understand why DATA is the most important to me. Watch this video and read the subtitles. Warning you will laugh your ass off.
@2:14 you will understand why us older guys put the database and database design before any other part of the app. You can have 100000000000000000000 interfaces to a database.
All an application is, is an interface to a database. All of this ORM and DDD and other crap sounds good on a resume but is useless if you don’t have data.
To do DDD you must first know your domain. You learn the domain by building a crappy quick version first.
This does show an interesting way to learn doctrine.
I’ve been designing databases for more than 10 years now. I studied the subject in depth and still own many books on the it.
I think in terms of data and data manipulation needs while designing apps in order to design the best system.
I decided to try to get doctrine to import the mapping.
The Symfony docs say it should work about 80% and you will need to fill in the rest. It doesn’t really say what the rest is, or give any tips. It kind of assumes you are a doctrine Wizard.
The issue is, you must damn near be a Doctrine Wizard in order to understand exactly how the Entities should be modeled in order to match a database design. Otherwise you are like me and have to do this by trial and error wasting lots of time, like many things Symfony.
I am a Wizard
If you have a sloppy Entity you have a sloppy database design. I had to learn how Doctrine would like an entity to be designed to match my database design.
Well the first issue I ran into was using foreign keys as a composite primary key.
Doctrine was all like
Ok so I changed my design so that every table had a unique ID instead of a unique foreign key primary key etc. I deleted most of my entities.
Then I run the command to rebuild the entities aka map them.
Now doctrine is not trying to change tinyint to smallint… but it wants to rename ALL of my foreign keys. WTF.
Ok that isn’t so bad, maybe it needs them named a specific way. I didn’t google that mess.
Lets get Cryptic with it.
I can live with cryptic foreign key names like…
Not like I have to see them often. Other than when there is a DB error now instead of my beautiful Foreign key scheme which included the table name as a hint I get this cryptic BS.
Lets be Null together
But then I look closer and now the diff command wants to change all of my foreign keys to default null.
The purpose of a foreign key is to make the database make sure a value exists in one table before inserting a value that links to it in another table. This is known as referential integrity.
This is where I draw the line with Doctrine
Allowing nulls on a foreign key is STUPID. This means that now your application code must make sure a value is not null before inserting it and it must exist in the other table first.
Which means you can’t use some of symfony’s validation techniques because they rely on the same Entity class annotations. Sure you can add to the form validation, but you still need to make sure a value exists in the linked table before you can insert it or else you insert a null value, which destroys referential integrity.
Null values on foreign keys just make no sense to me.
So in order to use Doctrine it looks to me like I must ruin my beautiful table design. I only wanted to use it for the migrations and some ease of use deals. But I do not want to destroy my database to do so.
For example if I have a hashtagged images table and that table contains a hashtag_id and an image_id . A table like the following
Why would I ever want either of those to be null? That is like dividing by zero. You never ever, ever want either of those null, that will make your database a flaming hot mess. So now you must move the check from the database to your code. This WILL eventually, one way or another lead to problems.
Now your code must check whether anything contains a null before your app can use it. It must also make sure that before you insert a hashtag for an image that you must first hit the hashtags table to make sure a hashtag with a specific id exists. Then you must hit the images table too, to make sure an image with such an ID exists. Then you can finally insert your images hashtag.
That is a ton of work when a simple “not null foreign key” constraint would do all of that for you. It would also shift the query work from the app to the database like it should be.
Not gonna do it…
So that is how I came to prefixing all tables with t_ so that Doctrine would leave them alone.
But I have this disorder that makes me INSANELY persistent.
I refused to leave this alone. So I kept making changes and running this command
This tiny little line nullable=false changes everything. It tells Doctrine you do not want that field to be nullable.
I don’t know why the Doctrine mapping code can’t get this right. This information is provided to it by the DB query. Maybe I could help find this code and update it so that it includes this tiny line if the DB query returns it.
You also need to fix EVERY LAST ENTITY NOW. Yes for each and every entity you must now open the file and list the damn repository for it. For some reason the Doctrine code can’t do this either, even though the Repository is just the Entity name with Repository added to it.
So open up every last single Entity and create a line like this above the class definition in a comment