Need Help With OOP Best Practices

Yo! I have some questions about different approaches and best practices using OOP.

Methods and arguments
I want to update different columns in my person table, and I wonder which methods and arguments I should use.

Approach 1:
A generic method that can handle all column updates

    $personTable->save($person);

Approach 2:
A specific method for each kind of update

    $personTable->saveName($person->name);
    $personTable->saveLocation($person->location);
    $personTable->saveNameAndLocation($person->name, $person->location);

I was thinking about combining both approaches, using approach 1 whenever I have 2 or more columns to update, but using appraoch 2 when I only have 1 column to update.
I guess it might be hard to decide without knowing how many times these methods will be called, so let’s assume this:

  1. saveName() called twice or more
  2. saveLocation() called twice
  3. saveNameAndLocation() called once

If I choose to use appraoch 2 at all, is it really necessary to have both saveName($name) and saveLocation($location)? I could just go with saveColumn($value, $column) and use the $column argument to choose the column. I think this makes the code a lil bit dirty, but I’m not sure if it’s really bad to do.

Method names
Inside my PersonTable class I currently have getById($id), getByName($name) and create($name).
The reason it’s called create($name) and not createByName($name) is because I never create the person in any other way, unlike the get methods that needs the “byId” and “byName” suffix. If I ever want to add another way to create a person I’ll add an suffix, but maybe it would be best to just use the suffix from the very start, it might be a bit easier to maintain and it will be more consistent, but I feel like it’s a bit ugly xD

Edit: Do you think it’s better to name the methods savePerson() instead of just save()? I do $personTable->save() and I think that’s cleaner and obvious enough.

I had some more questions, but I forgot them, and this is already long enough as it is. Thanks for reading. ^~^

I think the concern is basically how abstract should your methods/ interface/ program be? I am a fan of composition, which does require a level of abstraction. What you want to avoid, is having to change your code, due to external changes, if it is at all possible. For instance, what happens, if your “Person” table’s name changes to “Personnel” or “Students” (for whatever (stupid) reason). How many places would you need to change your code, if that would happen (and you still want your code to stay logical within its domain)? If at all, if external changes are made, then you should have only at most one place you need to change your code. Follow SRP. (although not completely the right principle, it does lead to the right thinking, which causes you to think a bit more abstractly.)

You mentioned a method of abstraction and said “that makes the code dirty”. Abstraction isn’t always the right way to go, but if it is necessary to decouple your modules or systems (and it usually always is), then it is the right way to go. Abstraction doesn’t make the code dirty. It decouples it, which, over time, is important.

The other thing abstraction helps with is code reduction. Think about how many other tables and save methods you might need to write (and come up with names for) with every new feature or table you create. Is that really necessary? Couldn’t you “abstract out” your “saving” methods to another class responsible only for any saving of any table? Just one class? :wink:

Think of this - compose two classes, one an entity class with the properties (the table class) and the other an entity manager, which holds the methods necessary to do any CRUD operations. Each CRUD operation probably being its own object too. Can you imagine that?

Thinking abstractly and making it a reality in your code is a core aspect to OOP. :smile:

Scott

1 Like

Nope, I can’t imagine that at all! Are you telling me that I should use 1 entity class to be able to handle all tables? But that would mean it would require a columns argument. So I would need to do something like new tableEntity(‘person’, array(‘id’, ‘name’, ‘location’)); every time, but I don’t want that, I just want to do new tableEntity;. I guess I could make 1 factory for every table, and inside the factory I create the table dependencies, but that would but me back to square one; I need to correct the factory name and all code that uses it every time I change the table name. I really need some code to understand, can you please show me at least how to create and use the necessary objects?

I don’t think this is what he meant. You create multiple entity classes, one for each table but only one entity manager class. This entity manager class can do CRUD operations on any of your entity classes. This way you separate data (entities) from database operations (entity manager).

1 Like

Alright, I understand what you mean now and thanks for the advice. It doesn’t really answer my questions though, my question is not really about entities but rather 1. should I use generic methods and pass an collection of things as argument, even in situations where I only need to pass a smaller collection of things or just one single thing? Or should I use specific methods and only pass the things I really need? 2. Should I use suffixes/prefixes for my method names and should I be consistent with using suffixes/prefixes even when there’s no ambiguity?

It depends.

In a way, this question is actually an incarnation of the famous “ActiveRecord vs. DataMapper” holywar.

And with DataMapper approach you indeed can have a single data manipulation class that can save an entity following some simple considerations like an entity name become a table name while property names making columns.

Of course you can create separate EntityManager for each entity, but personally I find this approach disgusting. For such a case I’d prefer an AR-based approach.

So basically you have three options:

  • classic AR: each entity extends the prototype Model class which have basic CRUD methods implemented.
  • classic DM: Each entity have a twin class EntityMapper, which extends the prototype Mapper class
  • optimized DM: one basic Mapper which provides basic crud methods for all entities by default, and which can be extended to satisfy the special needs.

I highly recommend using Doctrine. Writing your own ORM is a waste of time.

Writing your own tools makes you a programmer.
Using ready-made tools keeps you an apprentice.

It’s good to use Doctrine and to learn from it. Yet I am in full support for everyone who is trying their own ways.

3 Likes

Encouragement is definitely a good thing when learning.

2 Likes

I disagree.

Its not considerate to employers nor future devs to use proprietary code for an ORM. There won’t be any documentation, it will be riddled with bugs/oversights, and no one will know anything about it besides the creator. With Doctrine you get a community of resources, standardization, advanced features, and good documentation. None of that is going to exist with a half ass, one off ORM created by someone who doesn’t even know some basic design patterns commonly used throughout ORMs.

Its really just selfish and inconsiderate behavior.

Nobody says that a newly written tool have to be used in production.
I only said that it have to be written :wink:

Seriously, if everyone were thinking this way, we’d have no Doctrine to use as well.

4 Likes

Well… no

Doctrine was created out of the need for a ORM in a sea of ActiveRecords. Doctrine was one of the very first PHP ORMs at a time when the market was dominated by ActiveRecords.

Though I wouldn’t understand the vast majority of people on this forum to understand the importance of creating maintainable, scalable software since it seems lately to be mostly flooded with a bunch of beginners working alone.

Not to mention all these questions would be answered using Doctrine.

I think creating separate methods for saving each column and/or combination of columns can become quite tedious because you may end up with many of them. Using a data mapper approach it’s generally simpler to have just one method to save the whole collection of data in an entity:

$mapper->save($person);

Entity objects should be complete (at least in theory) containing all column data so there is no need to specifically pass any one of them to the mapper class because you pass the whole entity to the saving method. If the mapper is smart enough it can detect which values have changed and save only them in the database.

1 Like

Correct. Thanks for the support. :thumbsup:

Scott

Maybe I should have given some context… I’m not trying to make something serious at all XD It’s just toying around, trying to improve my OOP knowledge, and my current personTable is really just a dirty fast way to handle database queries. My questions where never really about that stuff… I never tried to make it good, I never tried to improve it and I was actually very aware that it sucked! If I wanted to develop an actual website I would use a framework like symfony 2, laravel or something like that and use their implementation of ORM.

I just used the personTable as an example, because It’s one of the objects that has a very generic method and I thought it would be good to use it for asking my question… But I guess not. Sorry I’m not very good at making myself clear with my questions…

What if it’s not a data mapper object but some other type of object, would the same thing still apply?

What do you mean by other type of object? There are basically two possibilities here:

  1. You save the data object from the outside - you are using a different object from the entity to do the job. Whatever you call it it is a form of data mapper.

  2. You save the data object from the same object - this is an active record style.

The above would apply to an ORM.

However, you don’t need to create an ORM, you can make your own model objects that will be responsible for data manipulations. So yes, of course, you can use separate methods for every use case. Depending on your requirements this can actually have some benefits over generic ORM-style CRUD: you can fine tune your model to provide exactly the methods that are required by your application - not more, not less:

$personModel->saveName($id, $name);
$personModel->changeLocation($id, $location);
$personModel->increaseVisitsCount($id, 1);
$personModel->ban($id);
$personModel->sendMessage($id, $subject, $message);
$personModel->create($personData);

In this case you have no entity objects and no real mappers, only a model class that has specific methods needed by your application. How you implement actual database manipulations is independent from the model interface - you can use an ORM with entity objects or just plain SQL. As you may have noticed, you can make the methods names quite descriptive and real, and their implementation is not limited to what an ORM can do - however, you can use an ORM if you want.

BTW, I don’t know if you were interested in an ORM specifically - I mentioned it because the discussion side stepped in that direction…

When I say another type of object I mean an object that has a completely different task. Maybe a controller object or a validator object or really any other thing that’s not a data mapper.

And what is that “same thing” you want to apply to this another type of object?

Whatever. I’ll just do what I was thinking in my original post:
If I need to change state of A and at least 2 or 3 values is required to do it, I’ll use a generic method and pass a collection to it.
If I need to change state of A and only 1 value is required to do it, I’ll use a specific method that only accepts that 1 value.

$valueCollection = ['asdf', 34, 'a'];
$a->do($valueCollection);

$val1 = 21314;
$a->doOne($val1);

$val2 = 2134124234;
$a->doTwo($val2);

I’m sure you don’t think I make any sense, judging from all the confusion from both me and everyone helping me out. I’m sorry, I’m really bad at explaining and communicating in general. Anyway I’m done with this thread. Thanks for all the help.