Where to put a growing list of queries (mostly all "SELECT" queries) and how to properly access them throughout my app?
I'm looking to re-factor my app which currently has queries sprinkled all throughout services in my app. I'm starting to encounter DRY issues where I have small and big queries that are similar to other queries I've already written. I also have places where I'm re-writing the same formatting on those queries over and over again. Although I'm not worried about replacing my choice of database, I do worry about later optimizing my queries and wonder if I've made that near impossible by not organizing all my queries better and keeping them in a central place somewhere. I want to follow good OOP and SoC practices.
An example of where I currently have a query in my app
For example, I have a service that provides a PDF (or the content rather) of all the staff at a certain event. The service has a method that accepts some input (like "sort", "filter", "pagination limit") and will perform a query with 10 table joins (the query slices fairly well down to a manageable number of rows before most of the JOINs work on it) and format the results in the desired manner for the PDF.
What I have planned so far
I'm having trouble trying to think perfectly about my database needs in terms of objects. I do a lot of flexible querying, not all completely ad-hoc, but a wide-range of SELECTs with many JOINs and WHEREs, but I'm trying my best:
Domain Objects: I will create classes like
usersEntity,eventsEntity(each match tables in my database) and setup properties in them likeid,name,phone,etc. that match the database table fields. I may create getters/setters so I can mutate (format things like dates, etc or perform validation). I might create entities that don't really represent a single table in my database, but perhaps represent a common collection of data that I find I re-use a lot throughout my app. Maybe call itusersCollectionorusersEvents.Data Mapper: So I put all the CUD (Create Update Delete...not Read...except maybe a findById) operations and stored them in classes called
usersMapper,eventsMapper, and so on. The mappers accept only 1 type of domain object (see #1 above) and will handle the persistence (and maybe a small set of very basic Reads/SELECTs, likefindById($id)orfindByEvent($eventId)).
Now this leaves me with a growing list of Reads (SELECT queries) and where to put them?
The options that I'm aware of, but fall short. Maybe I'm misunderstanding them.
Let's say I have a query like this
SELECT users.first_name, users.last_name, events.name, events.date, venues.name, venue_types.name, GROUP_CONCAT(user_friends.last_name) as 'friends' FROM users JOIN events ON(events.id = users.event_id AND events.date = :date) JOIN etc....
So what I see in blogs throughout the PHP community is 3 choices:
- Replace my query calls with something like Doctrine2 calls. Their DQL doesn't seem to make the call reusable? And things seems to be connected in a way that it would return like 5 entities (based on JOINs) with all properties filled for users, events, venues, venue types, friends. That's a lot of fields I don't need, I just wanted 1 column from each as you can see in my query above. Also, I don't plan on saving any entities after a query like this.
Replace all my query calls with Repository classes that have methods like:
$someUserRepo-> findNameAndEventNameAndEventDateAndVenueNameAndVenueTypeNameAndFriends(). Besides the really crazy method name, I'm not sure what I pass here (conditions like: sort, limit, a few where's?) or what I return (arrays? objects?).Replace all my query calls with ORM calls (like Laravel's Eloquent) using their query builders. I don't see how I gained anything by replacing my SQL query with
please->ORM->build->this->for->me->where->andWhere->andWhere->I->dont->know->SQL. By the way, I'm almost convinced that ORM's are an anti-pattern?
I suspect this is a just a very complicated problem throughout all of the PHP community. Or what am I missing here?