Categories
Software Development Web Development

How to create 100+ Symfony 5+ Doctrine 2 or 3 Repositories

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.

too many words meme
Not enough time to read it all.

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.

too fast meme
As a lazy programmer I like to go fast. LOL

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.

wow dog meme
Entity Cloner is very helpful too…

Repository Maker in action

Categories
Web Development

how to fix doctrine symfony entity has a repositoryClass set to “entity name”, but this is not a valid class. Check your class naming

So you got this error while trying to access a repository from a Service or somewhere else. So what went wrong and what does this mean?

baby what does that even mean meme
What does this even mean?

Well it probably means that you did not use a fully qualified name for the repositoryClass statement or you do not have one at all.

Basically Doctrine needs to know where the repository for an Entity is located. It can be anywhere. If you define the repository like this

/**
 * User
 *
 * @ORM\Table(name="user", uniqueConstraints={@ORM\UniqueConstraint(name="email_UNIQUE", columns={"email"})}, indexes={@ORM\Index(name="email", columns={"email"})})
 * @ORM\Entity(repositoryClass=UserRepository::class)
 */

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.

use App\Repository\UserRepository;

You can also just add the fully qualified Repository name like this.

@ORM\Entity(repositoryClass="App\Repository\UserRepository")

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

Links

You can find more about this error here.

Categories
Software Development Web Development

Tips and tricks to reverse engineer your existing database with Doctrine for Symfony

So in the Symfony documentation there is a page that describes how to reverse engineer an existing database using Doctrine.

borat not meme
Symfony doctrine NOT Joke with Borat

Rule #1 No DB Vendor custom types

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.

Seriously trying to figure out how to work around TinyInts and Enums is a real pain in the ass, I’d advise against it. This documentation page is written poorly it leaves lots of questions to be asked.

symfony documentation meme
Doctrine is all like WTF do you need docs for?

Like WTF does this line even mean?

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?

details meme
I need more details than that please. LOL

Use a backup

the office meme
Step 1 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.

Delete Migrations

system32 meme
Delete time

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.

No TinyInts

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.

angry cat not going to happen
Doctrine is all like… NOPE
The Answer

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.

NO ENUMS

no enums meme
Remove Enums from your database design first

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.

The Answer

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.

why can't i have my foreign key names?
Why can’t I have my foreign key names?

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.

The Answer

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.

Answer

Use this Doctrine/Symfony maker I created. It can loop through your Entities and create Repositories for them.

There is still one catch though. You will have much fun like I did, opening 100+ Entity files and adding the proper annotations for the matching Repository to it.

This part is so tedious and fun

For example my app has a PageUrls Entity and it uses… PageUrlsRepository so to fix PageUrls so that I can use custom Repository methods I must now add this in the annotations.

/**
 * PageUrls
 *
 * @ORM\Table(name="page_urls", indexes={@ORM\Index(name="userOwnerId", columns={"owner_user_id"})})
 * @ORM\Entity(repositoryClass=PageUrlsRepository::class)
 */

So I must do this 100+ times now. I hope you have less tables in your database schema

hide the pain meme
Repetitive stuff is fun

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.

Categories
Software Development Web Development

How to reverse engineer a database for a symfony project with doctrine… aka t_

TLDR

This is a parody about how I learned to Reverse engineer a Database for a Symfony App with Doctrine. It was an extreme  pain in the ass. Because …. IT IS NOT DOCUMENTED AT ALL

here we go again meme
Documentation is so overrated

Here is a short article including tips and tricks I discovered I learned while writing this.

This article jokes about some of what I ran into and what I was thinking while I learned Doctrine. It does give some details and useful information. t_ is how you tell Doctrine to ignore a table in migrations. I was going to just tell Doctrine to ignore all my tables. LOL

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.
The story

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.

look a wizard meme
Symfony thinks we are Doctrine wizard. LOL

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.

i am wizard meme
So I became a doctrine wizard… sort of LMFAO

Well the first issue I ran into was using foreign keys as a composite primary key.

Doctrine was all like

kitten frew up meme
doctrine no like primary foreign key

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.

php bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity

Doctrine creates all my entities. I look them over, not really knowing what all the hell I am really looking at with the annotations and such.

makes sense to me meme
Yes this all looks good to me. LMFAO

Next I decided to diff the database to see what all would need changing.

Ok now Doctrine doesn’t like my tinyint values I used in my MySql database design.

tiny paper meme
Doctrine is all like Tinyint WTF?

So I go through and change every tinyint to smallint. Delete most of the entities.  Run the commands again. You can define your own types for Doctrine more on that in another post.

Lets Change the foreign keys

Now doctrine is not trying to change tinyint to smallint… but it wants to rename ALL of my foreign keys. WTF.

Lets rename the foreign keys for no F***ing reason

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…

FK_CA093F7E83172943

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.

borat great job meme
It is the little things in programming.
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.

are you kidding me
Freaking seriously

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
draw the line meme
Sorry doctrine. No I will not ruin my database design just to use you.

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.
makes no sense meme
That makes no damn logical sense!

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

hashtagged_images

  • id
  • hashtag_id
  • image_id

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.

nothing works meme
I kept trying and trying to make changes
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

php bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity

Then running a migration diff and viewing the changes that doctrine wanted to make in the migration up.

Then I got this idea.

smiley bright idea meme
Then I got an idea!!!

What if I try adding nullable=false to the join column definitions? I didn’t bother googling it or trying to find it in the damn docs. I just did it.

just do it meme
Let me just try this.

So after I added that and reran the diff, not the entity mapping. And BOOM Doctrine was no longer suggesting in the migration up that it should change the foreign key to default to null.

detecting much win meme
Boom I won.

So when you want your foreign keys which are an ID in another linked table to not be null like they should be. You should have a definition like this in your Entity

/**
     * @var \ImageHashtags
     *
     * @ORM\ManyToOne(targetEntity="ImageHashtags")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="image_hashtag_id", referencedColumnName="id", nullable=false)
     * })
     */

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.

but there is still more stuff to do

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.

doctine so confuse, no can do easy works

So open up every last single Entity and create a line like this above the class definition in  a comment

* @ORM\Entity(repositoryClass=PageUrlsRepository::class)

Otherwise you will get Errors every time you try to use a Repository with a custom method name.

Now I am a wizard

i am a wizard meme
Now I am a Doctrine Wizard

Here is an article about tips and tricks to reverse engineer your database with Doctrine.