Think about it from the different perspective and recall that there is no silver bullet design.
What are you trying to achieve by your design? Do you want to have your data completely consistent or optimized for performance?
I have tables for admin, agent, customer and tables for data storage like city, services, category, sub-category, gender.
Admin, Agent and Customer sounds like entities in your domain. You will implement some business logic around them, so it makes sense to define appropriate classes. Usually data for each of such classes is contained inside single table in DB (could not be the case for derived entities).
City could be seen differently. Does city have some properties, like references to services or category (exactly this direction of relationship) or like country name? If I try to remove city from DB, should I remove all customers associated with city or at least prevent removal of city? If the answers for these questions is "Yes", then city has some properties, thus it is some entity in your business domain and it should be defined as class. If the answer is "No" and you know that city in your domain is just name of city, then you don't have to define city as class.
From programming perspective, would you like to work with something like
$customer->getCity()->getName() (city defined as entity) or
$customer->getCity() (city defined as string)? In the first case, you will always have JOIN at some point, either during fetching of
$customer from DB (eager strategy) or during call to
getCity() (lazy strategy). In the second case, city will be inlined in same table as customer, thus you will have better performance for bulk operations (like fetching 10K customers with your ORM).
Ok, city is pretty complicated entity to discuss without knowledge of your problem domain, but e.g. gender is definitely doesn't worth defining as class. How often are you going to change gender in your system? Do you even really need CRUD for it or is getting list of gender enough? From my development experience, we had gender as enum for 3 years without making any change to list of genders. It is pretty safe bet that you can define it as enum.
Concrete mechanism of connecting such enum as Gender to property depends on your framework of choice. E.g. in Doctrine you could use https://github.com/fre5h/DoctrineEnumBundle/ to implement abstract enums for property. Concrete values of choices for enum could be fetched from database during cache warmup or app initialization, for example.
Remember, the more interconnected entities you have, the more performance problems you will have in future (because of JOINs performed during every query). In the end, design can evolve, and performing data migrations is not that hard if you have suitably designed database on every step of application life (at least you have primary keys and foreign keys that reflect your domain). The best way to start designing you app/database is:
- Understand problem domain (what entities are there, what data flows you have).
- Build ERD of your business domain (good starter book on the topic could be "Learning MySQL" by Hugh Williams, Seyed Tahaghoghi).
- Create first version of database schema and evolve it over time accordingly to upcoming business requirements.
- Add app-database connection code to fit your database design (usually database lives longer than code).
Another good book for reading is "Refactoring Databases: Evolutionary Database Design" by Scott W. Ambler, Pramodkumar J. Sadalage.
TL;DR Don't try to fit design accordingly to tools that you have, instead create design that is appropriate for your problem domain and pick tools that will help to implement it.
Hope this provides some insight.