OOP approach in PHP

I have been programming PHP procedurally (is that even a word?) For about five years and decided to try the OOP approach, but ran into some concept / design problems. Let's say you have some modules in the program, each module has the ability to list, add, edit and delete an entity. An entity may be ... not a user, customer, product, etc.

How do you create classes to manage these entities?

Two possibilities appeared in my mind:

  • create classes for each object using methods such as getUsersList, addUser, editUser, delUser
    This approach seems resource-intensive because in the script you only need the getUsersList and possibly delUser methods to enumerate, while in the pop-up add user script you only need the addUser method, and in the pop-up window for user editing, the script editUser method. So, you have to create an object and use only one or two methods ...
  • create general classes: list, add, edit and delete and extend them for each entity in such a way that you only need to initiate one class at a time (the one you really need)

Thanks in advance,

+3
source share
4 answers

interface, , , . "". (, , ..) , .

"API" , . , , , , .

:

"" . , , , .., , , User, , , , ..

, "" - , .

$bob = new User('bob');
$bob->add(); // Adds bob to the database
$fred = new User('fred');
$fred->add(); // Adds fred to the database

$users = User::list(); // Gives an array of all the users in the database

.

+5

. . PHP (Symfony, CakePHP ..) , , ORM (Doctrine, Propel ..).

, , .

DbRecord, (, , ..). DbRecord .

class DbRecord {
    public function save() {
        // save logic (create or update)
    }

    public function delete() {
        // delete logic
    }

    // other record methods
}

class User extends DbRecord {
    private $name;
    private $email;

    public function setName($name_) {
        $this->name = $name_;
    }

    public function setEmail($email_) {
        $this->email = $email_;
    }
}

:

$user = new User();
$user->setName('jim');
$user->setEmail('jim@domain.com');

$user->save();

DbTable, (, ..).

class DbTable {
    public function readAll() {
        // read all
    }

    public function deleteAll() {
        // delete all logic
    }

    public function fetch($sql) {
        // fetch logic
    }

    // other table methods
}

class UserTable extends DbTable {
    public function validateAllUsers() {
        // validation logic
    }

    // ...
}

/:

$userTable = new UserTable();
$users = $userTable->readAll();

foreach ($users as $user) {
    // etc
}

- -. .

, , . PHP- ORM.

: DbRecord DbTable - - w/e, .

+2

, . , .

class User {
    function __construct() { /* Constructor code */ }
    function load($id) { ... }
    function save() { ... }
    function delete() { ... }
}
+1

" " ( , , ).

, , .

- ActiveRecord, () create-update-delete. , . , , , update() .

- , AR- ( Doctrine, ORM..), .

...


/**
 * Interface for all entities to use
 */
interface Entity {
    static function newEntity();
    static function fetch($id);
    function save();
    function setProperties(array $properties);
    function delete();
}


/**
 * A concrete product entity which implements the interface
 */
class Product implements Entity {
    public $productId;
    public $name;
    public $price;
    public $description;

    /**
     * Factory method to create a new Product
     *
     * @param integer $id Optional, if you have auto-increment keys you don't need to set it
     * @return Product
     */
    public static function newEntity($id=NULL) {
        $product = new Product();
        $product->productId = $id;
        return $product;
    }

    /**
     * Factory method to fetch an existing entity from the database
     *
     * @param integer $id
     * @return Product
     */
    public static function fetch($id) {
        // make select with supplied id
        // let $row be resultset
        if (!$row) {
            return NULL; // you might devise different strategies for handling not-found cases; in this case you need to check if fetch returned NULL
        }

        $product = new Product();
        $product->productId = $id;
        $product->name = $row['name'];
        $product->price = $row['price'];
        $product->description = $row['description'];
        return $product;
    }

    /**
     * Update properties from a propreties array
     * @param array $properties
     * @return void
     */
    public function setProperties(array $properties) {
        $this->name = $properties['name'];
        $this->price = $properties['price'];
        $this->description = $properties['description'];
    }

    public function save() {
        // save current product properties to database
    }

    public function delete() {
        // delete product with $this->productId from database
    }
}

/**
 * An abstract CRUD controller for entities
 */
abstract class EntityCrudController {
    protected $entityClass = 'UNDEFINED'; // Override this property in child controllers to define the entity class name
    protected $editTemplate = NULL; // Override this to set an edit template for the specific entity
    protected $templateEngine; // Pseudo-Templating engine for this example

    /**
     *  Display the edit form for this entity
     * @param integer $entityId
     * @return string
     */
    public function editAction($entityId) {
        // Fetch entity - this is not the most clean way to fetch, you should probably consider building a factory that encapsulates this.
        $entity = call_user_func($this->entityClass, 'fetch', $entityId);

        // Assign entity to your edit template, in this example I'm assuming we're using a template engine similar to Smarty
        // You can generate the HTML output in any other way you might like to use.
        $this->templateEngine->setTemplate($this->editTemplate);
        $this->templateEngine->assign('entity', $entity);
        return $this->template->render();
    }

    /**
     * Update an existing entity
     *
     * @param integer $entityId
     * @param array $postArray
     * @return string
     */
    public function updateAction($entityId, array $formArray) {
        // Be sure to validate form data first here, if there are errors call $this->editAction() instead and be sure to set some error information
        $entity = call_user_func($this->entityClass, 'fetch', $entityId);
        $entity->setProperties($formArray);
        $entity->save();

        // Again, using our imaginary templating engine to display...
        $this->templateEngine->setTemplate($this->editTemplate);
        $this->templateEngine->assign('entity', $entity);
        $this->templateEngine->assign('message', 'Saved successfully!');
        return $this->template->render();
    }

    // Devise similar generic methods for newAction/insertAction here
}


/**
 * Concrete controller class for products
 * This controller doesn't do much more than extend the abstract controller and override the 2 relevant properties.
 */
class ProductCrudController extends EntityCrudController {
    protected $entityClass = 'Product';
    protected $editTemplate = 'editProduct.tpl';
}

// Usage example:

// Display edit form:
$controller = new ProductCrudController();
$htmlOutput = $controller->editAction(1);

// Save product:
$htmlOutput = $controller->updateAction(1, array('name' => 'Test Product', 'price' => '9.99', 'description' => 'This is a test product'));

Of course, much can be improved. you usually do not want to make a request every time you call fetch () on an object, but instead request it once and save the resulting object in IdentityMap , which also ensures data integrity.

Hope this helps, it worked out a little more than I expected, but I think it is commendable that you are trying to solve this problem without creating a framework for this problem :)

+1
source

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


All Articles