CRUD and OOD. How to approach this?

Please be rude honest and tear my work, if you need.

So, I am rewriting a small web application that I recently made. The reason for this is because the code got pretty dirty and I want to learn and apply the best OO design. What this application should do is just CRUD. I have a database with three tables, companies and partners , which are not related to each other and city , which has a 1: n relationship with companies and partners. Very simple. Now I have a few questions that I will indicate at the end of my post. Here I will just try to explain:

My first approach was that I created the company, partner and city classes, pulled out all the data sets from the database and created objects from them:

 class company { private $id = null; private $name = null; private $city = null; //many more attributes function __construct( $id, $name, $city, [...] ) { $this->id = $id; $this->name = $name; $this->city = $city; //huge constructor } /* * getters + setters here * * no need to paste the partner class as it looks just like this one * */ } 

And these are all these classes. I extracted each data set from the database and built the company, partner and city objects (the attribute city in these classes is an object with several attributes) and saved them in two arr_companies and arr_partners , which then held these objects ... and it worked fine .

Now I would like to update, insert, delete into the database, and all 3 classes (city, company, partner) need this function. My approach was that I created a new class with a constructor that basically would take two lines of command and object, for example. ('update', 'company') , and he will then update the company directly in the database, leaving my objects untouched. This really upset me, because I had such beautifully designed objects, and I did not know how to use them.

Questions:

  • Is it bad to have such huge constructors (my biggest is 28 parameters)?

  • If you have a separate class for the database, or is it better to have, perhaps, an abstract class or an interface for it and let the subclasses themselves handle the update, deletion, insert?

  • Is it usually simple to write, delete from the database each time, or when I just apply these changes to my objects and only later execute commands in the database, for example, when the session ends?

  • I suppose an application like this must have been fantastic. What is the approach here? create objects, work with objects, save them in a database?

  • I have so many questions, but I think many of them I just don’t know how to ask.

Please note that if possible, I would not want to use ORM at this point.

Thanks so much for your time.

+4
source share
6 answers

Questions asked at OP:

"Is it bad to have such huge constructors (my largest of them will take 28 parameters) ??

  • Yes. Provide the calling code. You would have to pass 28 different values, not to mention the fact that each call would have to follow the exact order specified in the constructor. If one of the parameters was inappropriate, you could destroy the chaos with parameter-dependent algorithms. If you really need to pass a large number of parameters, I would recommend passing them as an array (sent an example to another SO question).

"If you have a separate class for database operations or it is better to have, perhaps, an abstract class or interface for it, and let the subclasses themselves handle updating, deleting, and inserting?"

  • Generally speaking, when creating classes, you want to try to identify the nouns that best suit your needs. In your particular case, you are likely to have three classes; Company, Partner and City.

  • Now in every class (noun) your methods will be in the form of verbs, so symantically your calling code makes sense: if ($company->getName() === 'forbes')

  • As you already mentioned, each class needs a database object (dbo) to work, so you can implement any number of templates to reveal connections to your classes; singleton, singleton with factory or dependency injection, etc.

  • Abstract (parent) classes are great for sharing common algorithms in child classes and should be identified when you are at the stage of creating an alias for your project. Parent classes also allow you to force child classes to have methods by declaring abstract methods inside the parent.

  • Interfaces are a useful tool in certain situations, but I find them less flexible than declaring abstract methods in a parent class. But they are good in situations where classes do not have a common parent.

"It is well known that just writing, deleting from the database each time or when I just apply these changes to my objects and only execute commands in the database later, for example, when the session ends"?

  • CRUD activity must occur during the execution of an action. If you wait until the session ends, you may encounter situations when the session ended ahead of schedule due to a user closing the browser, for example. To better protect your data, you can wrap your CRUD activity in a transaction.

  • If you use an application with high traffic, you can implement a queuing system and queue up the work that needs to be done.

"I believe that an application like this must have been done fantastically once before. What is the right approach here: create objects, work with objects, save them in a database?"

  • You are right, this has been done before, and they are usually called ORMs (object relationship mappers). Basically, ORM will analyze your database schema and create objects (and relationships) that represent your schema. Therefore, instead of working with your own SQL, you work with objects. Although you can use SQL for your custom business needs, in the case of Doctrine you should use the Doctrine Query Language (DQL) and native SQL.

  • The ORM I would recommend is Doctrine .

If you do not want to use ORM, you can add CRUD methods to your primary classes. I chose an interface, so your classes should not extend to a parent consisting of database operations. Also, check out this post about using singleton / factory to publish class database objects (s).

Consider the following:

 // Company.php class Company implements iDatabaseOperation public function delete() { // Lets use a DBO singleton/factory for DB access // Uses PDO, which is strongly recommended $dbo = Database::factory(Database::DATABASE_NAME); $dbo->beginTransaction(); try { $sql = "DELETE FROM " . " company " . "WHERE " . " id = :companyId " . "LIMIT 1"; $stmt = $dbo->prepare($sql); $stmt->bindValue(':companyId', $this->getId()); $stmt->execute(); $dbo->commit(); } catch (Exception $e) { $dbo->rollback(); error_log($e->getMessage(); $e = null; // Php garbage collection sucks } } } // iDatabaseOperation.php interface iDatabaseOperation { public function delete(); public function update(); public function insert(); } 
+5
source

You are essentially writing your own ORM. Therefore, I would not refuse to switch to the one that has already been written for you. The advantage associated with your own is that you understand how it works, how you write it. But the downside is that someone may have already done it better. But if you want to continue ...

General tip: Remember to always shift the problem to simpler and simpler parts. Each class should perform only a simple function. Plus, you don’t have to worry about caching updates ... if perhaps your database is not at the other end of the dial-up connection.

Specific advice should be:

I would set up the entity instance classes to host the data, and not to load a lot of data. Use other classes and logic to load data. I would use the constructor of the entity class only to populate the data related to the class (and its children).

The easiest way is to use static methods for an entity class to load and save data. For instance.

 class city { private $id = null; private $name = null; function __construct( $id, $name ) { $this->id = $id; $this->name = $name; } // getters and setters ... // --------------------- // static functions // --------------------- public static function loadById($cityId) { // pull up the city by id $retval = new city(row["id"], row["name"]); // close db connection return $retval; } public static function loadByCustomerId($customerId) { // pull up multiple cities by customer id // loop through each row and make a new city object // return a hash or array of cities } public static function update($city) { // generate your update statement with $city->values } // other methods for inserting and deleting cities ... } 

So, now the code for receiving and updating cities will look something like this:

 // loading city data $city = city::loadById(1); // returns a city instance $cities = city::loadByCustomerId(1); // returns an array of city instances // updating city data $city->name = "Chicago"; // was "chicago" city::update($city); // saves the change we made to $city 

Static methods are not the best way to implement this, but it forces you to point in the right direction. A repository template would be better, but that is beyond the scope of this one answer. I find that often I don’t see the benefits in a more attractive solution, such as a repository template, until I run into problems with simpler solutions.

+1
source

What you do looks greedy. You can add an intermediate layer that maps your business object to your database (object relationship mapping). There is a lot of relational mapping of api objects. Check out this wikipedia list for those you can use for PHP

0
source

I think that a constructor with 28 parameters is too much, you need other classes to manage some attributes that have some common features. You should tell us what other attributes are attributed to you, and this can help you find a way to create more general objects.

I think you should also create a class that manages operations and the database, for example, DBHandler with deletion, updating, etc. In my opinion, modifications to tuples in your database immediately after calling functions are important.

Why? Since it can avoid conflicts, for example, if you are trying to update an object that should be deleted, for example, if you make changes to your database at the end.

0
source
  • This is very bad. In this case, the code is not readable. You have options
    • use setters (you can add validation logic inside, improve readability, no need to fill in empty fields with zeros)
    • have a separate class-class for each class of the domain (takes some memory for an additional object). An example in java hope you can understand:
        class CompanyBuilder {
       private final Company c;
       public CompanyBuilder () {
        c = new Company (); 
      } CompanyBuilder addId (String id) {c.id = id;} // id should be package visible and class should be located in the same package with builder CompanyBuilder addName (String name) {...} CompanyBuilder addCity (String city) {...} Company build () {return c;} }
    • a hybrid solution to have chaining methods (worst debugging, better readability). There will be methods in java:
        class Company {
       ...
       Company addId (String id) {
       this.id = id;
       return this; 
      } Company addName (String name) {...} ... } Usage: Company c = new Company (). AddId ("1"). AddName ("Name1");
    • perhaps you can create more granular objects to reuse them later and add certain logic to the right place. For example, it could be an Address (Location) object for a company.
  • Follow the principle of shared responsibility. SOLID description on the wiki . This helps to change the database code without being tied to another part of the system in your case. Well, separate domain code and databases have a common interface or an abstract class (if you have common logic for all classes of a domain - the Liskov principle). Subclasses implement a specific part of the domain.
  • If you do not want to lose data, you must save it every time or have a server cluster or distributed cache. If this is normal, then lose them at the end of the session as a batch. This will increase your productivity. You should also save the transaction every time if you have concurrent updates.
  • The approach is to obtain data from database objects / objects from this data or objects from new objects / work (updates) / write data from objects to the database
  • just write more code and read stackoverflow

Finally, I suggest reading “Clean Code: A Guide to Flexible Software Skills” by R. Martin.

0
source

You can watch ruby on rails .

You don’t have to switch to it, but watch how they implement the MVC pattern and achieve CRUD.

0
source

Source: https://habr.com/ru/post/1390514/


All Articles