如何将数据库与模型分离以及如何在多个表上扩展模型 [英] How to separate database from model and extending model over multiple tables

查看:92
本文介绍了如何将数据库与模型分离以及如何在多个表上扩展模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前对模型使用本地化ORM方法,即,对象的属性映射到表的字段.这似乎是使用简单网站的一种非常可行且直观的方法.

I currently use a home-spun ORM approach to models, ie, an object's properties map to the fields of a table. This seems to be a very workable and intuitive way to work with simple websites.

但是...

随着复杂性的增加,当我想从联接表中获取数据时,这种方法变得很麻烦.我的数据库抽象只针对单个表,使用联接进行查询只是自然而然.

As the complexity increases, this approach becomes cumbersome when I want to get data from joined tables. My database abstractions are aimed at single tables only, and to do queries with joins just doesn't fall into place naturally.

我一直在调查DMM,特别是在 https://github.com/codeinthehole/domain-model-mapper ,但我在细节中迷失了方向.如果我能弄清楚我的问题是什么,我需要回答一些更大的问题:/

I've been investigating DMM, specifically at https://github.com/codeinthehole/domain-model-mapper, but I'm getting lost somewhere in the details. I need some bigger-picture questions answered, if I can just figure out what my questions are :/

因此,首先,MVC具有一个清晰的结构:模型,视图和控制器.您可以分离目录结构来分离关注点.但是,正如我正在学习的那样,该模型不是一个整体类.您想将存储与业务逻辑分开.

So to start, MVC has a nice delineated structure: Model, View, and Controller. You can separate out the directory structure to separate the concerns. But, as I'm learning, the model is not one monolithic class. You want to separate the storage from the business logic.

第二个问题与实际的数据库访问有关.根据DMM模式,您有一个内存中对象,可以通过调用->save()方法来保存该对象.此内存中对象不一定与任何数据库表进行1:1映射.这就是我迷路的地方... save方法可能会将内存中的对象注入到数据访问对象中,从而将对象持久保存在数据库中.

The second question is related to the actual database access. According to the DMM pattern, you have an in-memory object that you would save by calling a ->save() method. This in-memory object does not necessarily map 1:1 with any database table. And this is where I get lost... The save method would probably inject the in-memory object into the data access object which would in turn persist the object in the database.

借助数据库抽象,我可以使用自己的findfindallinsertupdatedelete等方法创建一个类,该方法可以针对每个数据库表进行扩展.但是DMM似乎是一个完全不同的范例. DAO是否有抽象,还是必须为每个应用程序定制设计?

With a database abstraction, I can make a class with my own find, findall, insert, update, delete, etc. methods that can be extended for each database table. But DMM seems to be a completely different paradigm. Is there an abstraction for the DAO, or does it have to be custom-designed for each application?

对于这两个问题,我都意识到它们是抽象问题.我不是在要求别人为我调试代码;我想了解其背后的理论.因此,很难提出容易使自己容易回答的问题.但是我欢迎您提供任何部分答案,只要它能使社区有机会与我一起学习即可.

For both of these questions, I realize that they are abstract questions. I'm not asking for someone to debug code for me; I'm wanting to understand the theory behind it. As such, it's hard to ask questions that lend themselves easily to answers. But I welcome any partial answers, as long as it gives the community the opportunity to learn along with me.

推荐答案

从一般的角度来看,基于MVC概念的Web应用程序由两层组成:模型层和表示层.它们的实现实现了关注点分离的目标.

From a general perspective, a web application based on the MVC concept is composed of two layers: the model layer and the presentation layer. Their implementation achieves the - goal of - separation of concerns.

模型层 包含三个子层:

The model layer consists of three sublayers:

  • 域层(域模型) 域对象(内存中的对象)-也称为模型.它们是封装业务逻辑的实体.因此,通过它们的结构和相互依存关系,它们是对现实世界(业务)对象/实体的抽象.该层还可以包含诸如域对象集合的结构.
  • 存储层,由负责将域对象与基础存储系统(可能是RDBMS,会话,文件系统等)进行传输的类组成: "https://martinfowler.com/eaaCatalog/repository.html" rel ="nofollow noreferrer">存储库,适配器,数据访问抽象类( PDO 服务层 是根据类(例如服务)构建的)执行涉及上两个子层的结构的操作.例如,服务从存储系统中获取域对象,根据其状态(属性)进行一些验证,然后返回相应的结果.
  • The domain layer (domain model), consisting of the domain objects (the in-memory objects) - also known as models. They are the entities that encapsulate the business logic. So, through their structure and interdependence with each other, they are an abstraction of the real world (business) objects/entities. This layer could also contain structures like collections of domain objects.
  • The storage layer, composed from the classes responsible with the transfer of the domain objects into/from the underlying storage system (may it be RDBMS, session, file system etc): repositories, (data) mappers, adapters, data-access abstraction classes (PDO, MySQLi - and their wrappers), etc. The use of these structures also achieve the purpose of making the domain objects (completely) agnostic, unknowledgeable to the storage type and the way in which it is addressed.
  • The service layer is built from classes (e.g. services) that execute operations involving the structures from the upper two sublayers. For example, a service fetches a domain object from the storage system, make some validations based on it's state (properties) and returns corresponding results.

表示层包括:

  • 观看次数.
  • 控制器.
  • [视图模型]
  • Views.
  • Controllers.
  • [View-models]

请注意,我没有完成对这一层的描述.我这样做是有目的的,因为我认为最好通过以下链接来获得对该主题的正确认识:

Notice that I didn't complete the description of this layer. I did it on purpose, because I think it's better for you to follow this links, in order to gain the correct perspective on this subject:

  • Understanding MVC Views in PHP
  • PHP MVC: Too many dependencies in controller?
  • MVC In PHP series
  • Model-View-Confusion Series

关于您的第二个问题:确实,ORM使域层和数据库之间的映射自动化.它们很有用,但也有缺点,因为它们迫使您根据业务逻辑PLUS数据库结构进行思考. "每个表一个类",如(表数据网关) ,"受保护的$ tableName; ",如类用户扩展ActiveRecord ",如活动记录等是灵活性限制的标志.例如,正如我在DMM代码中看到的那样,它迫使您在Mapper构造函数中提供$tableName$identityFields.那是一个很大的限制.

About your second question: Indeed, ORMs automatize the mapping between domain layer and database. They are useful, but also come with disadvantages, because they are forcing you to think in terms of business logic PLUS database structure. "One class per table" as in (Table Data Gateway), "protected $tableName;" as in the parent class Mapper of DMM, "class User extends ActiveRecord" as in Active Record, etc, are signs of flexibility limitations. For example, as I saw in the DMM code, it forces you to provide a $tableName and $identityFields in Mapper constructor. That's a big limitation.

无论如何,如果您想真正灵活地处理涉及(复杂)数据库查询的任务,请保持简单:

Anyway, if you want to be really flexible in tasks involving (complex) querying of database, then keep it simple:

  • 保持域对象完全不知道存储系统.
  • 实施数据映射器模式,而不继承任何父映射器的特定映射器!然后,在特定数据映射器的方法(保存,更新,插入,删除,查找,findByXXX等)中,您可以使用无限复杂的纯SQL .阅读 PHP MVC:数据映射器模式:类设计 .当然,通过这种方式,您将编写更多的sql ...,并成为一个SQL语言专家! :-)请注意,任何其他解决方案"都会降低sql的灵活性.
  • 如果您确实需要从sql语言(SQL,T-SQL,PL/SQL等)中进行抽象,则甚至可以实现自己的查询生成器类,并在数据映射器方法(而不是sql)中使用它的实例陈述.阅读 PHP MVC:Data Mapper的查询构建器类层.
  • 实现一个Adapter类,该类与 DMM . tableName之类的内容不应出现在其中.
  • 创建一个PDO连接,并将其注入Adapter对象的构造函数中.注意:不要像DMM那样在Adapter中创建PDO,因为它们的适配器类随后紧密耦合到PDO.您的-应该正确-松散耦合.您可以通过依赖项注入来实现这一目标-请参见干净的代码讨论-别找东西! .
  • 尝试使用依赖项注入容器.类似于 Auryn .它将仅在应用程序的入口点(index.php或bootstrap.php)中处理所有类的实例化和共享.最好的例子是PDO连接,它是通​​过Web MVC的整个周期共享的.它非常强大,非常易于学习和使用,并且会使您的MVC真正苗条.首先观看 PHP中的依赖注入和依赖反转.
  • Keep the domain objects completely unaware of storage system.
  • Implement the data mapper pattern without inheriting the specific mappers from any parent mapper! In the methods of the specific data mapppers (save, update, insert, delete, find, findByXXX, etc) you can then use pure SQL, infinitely complex. Read PHP MVC: Data Mapper pattern: class design. Of course, this way you'll write a bit more sql... and become an SQL-virtuoso! :-) Please notice that any other "solution" will reduce the sql flexibility.
  • If you really need to abstract from the sql language (SQL, T-SQL, PL/SQL, etc), you can even implement your own query builder class and use it's instance inside the data mapper methods, instead of the sql statements. Read PHP MVC: Query builder class for Data Mapper layer.
  • Implement an Adapter class, 90% similar to the one in DMM. Things like tableName should not appear there.
  • Create a PDO connection and inject it in the constructor of the Adapter object. NB: Don't create PDO inside Adapter, as DMM does, because their adapter class is then tight coupled to PDO. Yours should be - correctly - loosely coupled. You're achieving this through dependency injection - see The Clean Code Talks - Don't Look For Things!.
  • Try to use a dependency injection container. Like Auryn. It will take care of the instantiation and sharing of all classes at the entry point of your application (index.php, or bootstrap.php) only. Best example is the PDO connection, shared through the complete cycle of a web MVC. It's very powerful, very easy to learn and use, and will make your MVC really slim. Watch Dependency Injection and Dependency Inversion in PHP first.

稍后您还将要创建存储库和服务.

Later you'll want to create repositories and services too.

因此,结束第一个问题:有一个很好的说明文章系列,完全关于您感兴趣的内容.阅读它们之后,您将毫无疑问地了解所有模型层组件的工作方式一起.注意:您会在这里看到与属性相同的$tableName,但是现在您知道从哪个角度考虑它了.所以:

So, to close your first question: there is a very good explained article series, exactly about what you're interested in. After you'll read them, you'll have no doubt anymore about how all model layer components work together. NB: You'll see there the same $tableName as property, but now you know from which perspective to consider it. So:

  • Building a Domain Model – An Introduction to Persistence Agnosticism
  • Building a Domain Model – Integrating Data Mappers
  • Handling Collections of Aggregate Roots – the Repository Pattern
  • An Introduction to Services

这是一个映射器的版本,灵感来自以上文章.请注意,没有从父类/抽象类继承.要找出原因,请阅读 PHP MVC的出色答案:数据映射器模式:类设计.

And here is a version of a mapper, inspired from the above articles. Notice the absence of inheritance from a parent/abstract class. To find out the reasons, read the great answer from PHP MVC: Data Mapper pattern: class design.

<?php

/*
 * User mapper.
 * 
 * Copyright © 2017 SitePoint
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

namespace App\Modules\Connects\Models\Mappers;

use App\Modules\Connects\Models\Models\User;
use App\Modules\Connects\Models\Models\UserInterface;
use App\Modules\Connects\Models\Mappers\UserMapperInterface;
use App\Modules\Connects\Models\Collections\UserCollectionInterface;
use App\Core\Model\Storage\Adapter\Database\DatabaseAdapterInterface;

/**
 * User mapper.
 */
class UserMapper implements UserMapperInterface {

    /**
     * Adapter.
     * 
     * @var DatabaseAdapterInterface
     */
    private $adapter;

    /**
     * User collection.
     * 
     * @var UserCollectionInterface
     */
    private $userCollection;

    /**
     * 
     * @param DatabaseAdapterInterface $adapter Adapter.
     * @param UserCollectionInterface $userCollection User collection.
     */
    public function __construct(DatabaseAdapterInterface $adapter, UserCollectionInterface $userCollection) {
        $this
                ->setAdapter($adapter)
                ->setUserCollection($userCollection)
        ;
    }

    /**
     * Find user by id.
     * 
     * @param int $id User id.
     * @return UserInterface User.
     */
    public function findById($id) {
        $sql = "SELECT * FROM users WHERE id=:id";
        $bindings = [
            'id' => $id
        ];

        $row = $this->getAdapter()->selectOne($sql, $bindings);

        return $this->createUser($row);
    }

    /**
     * Find users by criteria.
     * 
     * @param array $filter [optional] WHERE conditions.
     * @return UserCollectionInterface User collection.
     */
    public function find(array $filter = array()) {
        $conditions = array();
        foreach ($filter as $key => $value) {
            $conditions[] = $key . '=:' . $key;
        }
        $whereClause = implode(' AND ', $conditions);

        $sql = sprintf('SELECT * FROM users %s'
                , !empty($filter) ? 'WHERE ' . $whereClause : ''
        );
        $bindings = $filter;

        $rows = $this->getAdapter()->select($sql, $bindings);

        return $this->createUserCollection($rows);
    }

    /**
     * Insert user.
     * 
     * @param UserInterface $user User.
     * @return UserInterface Inserted user (saved data may differ from initial user data).
     */
    public function insert(UserInterface $user) {
        $properties = get_object_vars($user);

        $columnsClause = implode(',', array_keys($properties));

        $values = array();
        foreach (array_keys($properties) as $column) {
            $values[] = ':' . $column;
        }
        $valuesClause = implode(',', $values);

        $sql = sprintf('INSERT INTO users (%s) VALUES (%s)'
                , $columnsClause
                , $valuesClause
        );
        $bindings = $properties;

        $this->getAdapter()->insert($sql, $bindings);

        $lastInsertId = $this->getAdapter()->getLastInsertId();

        return $this->findById($lastInsertId);
    }

    /**
     * Update user.
     * 
     * @param UserInterface $user User.
     * @return UserInterface Updated user (saved data may differ from initial user data).
     */
    public function update(UserInterface $user) {
        $properties = get_object_vars($user);

        $columns = array();
        foreach (array_keys($properties) as $column) {
            if ($column !== 'id') {
                $columns[] = $column . '=:' . $column;
            }
        }
        $columnsClause = implode(',', $columns);

        $sql = sprintf('UPDATE users SET %s WHERE id = :id'
                , $columnsClause
        );
        $bindings = $properties;

        $this->getAdapter()->update($sql, $bindings);

        return $this->findById($user->id);
    }

    /**
     * Delete user.
     * 
     * @param UserInterface $user User.
     * @return bool TRUE if user successfully deleted, FALSE otherwise.
     */
    public function delete(UserInterface $user) {
        $sql = 'DELETE FROM users WHERE id=:id';
        $bindings = array(
            'id' => $user->id
        );

        $rowCount = $this->getAdapter()->delete($sql, $bindings);

        return $rowCount > 0;
    }

    /**
     * Create user.
     * 
     * @param array $row Table row.
     * @return UserInterface User.
     */
    public function createUser(array $row) {
        $user = new User();
        foreach ($row as $key => $value) {
            $user->$key = $value;
        }
        return $user;
    }

    /**
     * Create user collection.
     * 
     * @param array $rows Table rows.
     * @return UserCollectionInterface User collection.
     */
    public function createUserCollection(array $rows) {
        $this->getUserCollection()->clear();
        foreach ($rows as $row) {
            $user = $this->createUser($row);
            $this->getUserCollection()->add($user);
        }
        return $this->getUserCollection()->toArray();
    }

    /**
     * Get adapter.
     * 
     * @return DatabaseAdapterInterface
     */
    public function getAdapter() {
        return $this->adapter;
    }

    /**
     * Set adapter.
     * 
     * @param DatabaseAdapterInterface $adapter Adapter.
     * @return $this
     */
    public function setAdapter(DatabaseAdapterInterface $adapter) {
        $this->adapter = $adapter;
        return $this;
    }

    /**
     * Get user collection.
     * 
     * @return UserCollectionInterface
     */
    public function getUserCollection() {
        return $this->userCollection;
    }

    /**
     * Set user collection.
     * 
     * @param UserCollectionInterface $userCollection User collection.
     * @return $this
     */
    public function setUserCollection(UserCollectionInterface $userCollection) {
        $this->userCollection = $userCollection;
        return $this;
    }

}

数据映射器界面:

<?php

/*
 * User mapper interface.
 */

namespace App\Modules\Connects\Models\Mappers;

use App\Modules\Connects\Models\Models\UserInterface;

/**
 * User mapper interface.
 */
interface UserMapperInterface {
    /**
     * Find user by id.
     * 
     * @param int $id User id.
     * @return UserInterface User.
     */
    public function findById($id);

    /**
     * Find users by criteria.
     * 
     * @param array $filter [optional] WHERE conditions.
     * @param string $operator [optional] WHERE conditions concatenation operator.
     * @return UserCollectionInterface User collection.
     */
    public function find(array $filter = array(), $operator = 'AND');

    /**
     * Insert user.
     * 
     * @param UserInterface $user User.
     * @return UserInterface Inserted user (saved data may differ from initial user data).
     */
    public function insert(UserInterface $user);

    /**
     * Update user.
     * 
     * @param UserInterface $user User.
     * @return UserInterface Updated user (saved data may differ from initial user data).
     */
    public function update(UserInterface $user);

    /**
     * Delete user.
     * 
     * @param UserInterface $user User.
     * @return bool TRUE if user successfully deleted, FALSE otherwise.
     */
    public function delete(UserInterface $user);

    /**
     * Create user.
     * 
     * @param array $row Table row.
     * @return UserInterface User.
     */
    public function createUser(array $row);

    /**
     * Create user collection.
     * 
     * @param array $rows Table rows.
     * @return UserCollectionInterface User collection.
     */
    public function createUserCollection(array $rows);
}

适配器类:

<?php

namespace App\Core\Model\Storage\Adapter\Database\Pdo;

use PDO;
use PDOStatement;
use PDOException as Php_PDOException;
use App\Core\Exception\PDO\PDOException;
use App\Core\Exception\SPL\UnexpectedValueException;
use App\Core\Model\Storage\Adapter\Database\DatabaseAdapterInterface;

abstract class AbstractPdoAdapter implements DatabaseAdapterInterface {

    /**
     * Database connection.
     * 
     * @var PDO
     */
    private $connection;

    /**
     * Fetch mode for a PDO statement. Must be one of the PDO::FETCH_* constants.
     * 
     * @var int
     */
    private $fetchMode = PDO::FETCH_ASSOC;

    /**
     * Fetch argument for a PDO statement.
     * 
     * @var mixed 
     */
    private $fetchArgument = NULL;

    /**
     * Constructor arguments for a PDO statement when fetch mode is PDO::FETCH_CLASS.
     * 
     * @var array 
     */
    private $fetchConstructorArguments = array();

    /**
     * For a PDOStatement object representing a scrollable cursor, this value determines<br/>
     * which row will be returned to the caller.
     * 
     * @var int 
     */
    private $fetchCursorOrientation = PDO::FETCH_ORI_NEXT;

    /**
     * The absolute number of the row in the result set, or the row relative to the cursor<br/>
     * position before PDOStatement::fetch() was called.
     * 
     * @var int 
     */
    private $fetchCursorOffset = 0;

    /**
     * @param PDO $connection Database connection.
     */
    public function __construct(PDO $connection) {
        $this->setConnection($connection);
    }

    /**
     * Fetch data by executing a SELECT sql statement.
     * 
     * @param string $sql Sql statement.
     * @param array $bindings [optional] Input parameters.
     * @return array An array containing the rows in the result set, or FALSE on failure.
     */
    public function select($sql, array $bindings = array()) {
        $statement = $this->execute($sql, $bindings);
        $fetchArgument = $this->getFetchArgument();
        if (isset($fetchArgument)) {
            return $statement->fetchAll(
                            $this->getFetchMode()
                            , $fetchArgument
                            , $this->getFetchConstructorArguments()
            );
        }
        return $statement->fetchAll($this->getFetchMode());
    }

    /**
     * Fetch the next row from the result set by executing a SELECT sql statement.<br/>
     * The fetch mode property determines how PDO returns the row.
     * 
     * @param string $sql Sql statement.
     * @param array $bindings [optional] Input parameters.
     * @return array An array containing the rows in the result set, or FALSE on failure.
     */
    public function selectOne($sql, array $bindings = array()) {
        $statement = $this->execute($sql, $bindings);
        return $statement->fetch(
                        $this->getFetchMode()
                        , $this->getFetchCursorOrientation()
                        , $this->getFetchCursorOffset()
        );
    }

    /**
     * Store data by executing an INSERT sql statement.
     * 
     * @param string $sql Sql statement.
     * @param array $bindings [optional] Input parameters.
     * @return int The number of the affected records.
     */
    public function insert($sql, array $bindings = array()) {
        $statement = $this->execute($sql, $bindings);
        return $statement->rowCount();
    }

    /**
     * Update data by executing an UPDATE sql statement.
     * 
     * @param string $sql Sql statement.
     * @param array $bindings [optional] Input parameters.
     * @return int The number of the affected records.
     */
    public function update($sql, array $bindings = array()) {
        $statement = $this->execute($sql, $bindings);
        return $statement->rowCount();
    }

    /**
     * Delete data by executing a DELETE sql statement.
     * 
     * @param string $sql Sql statement.
     * @param array $bindings [optional] Input parameters.
     * @return int The number of the affected records.
     */
    public function delete($sql, array $bindings = array()) {
        $statement = $this->execute($sql, $bindings);
        return $statement->rowCount();
    }

    /**
     * Prepare and execute an sql statement.
     * 
     * @todo I want to re-use the statement to execute several queries with the same SQL statement 
     * only with different parameters. So make a statement field and prepare only once!
     * See: https://www.sitepoint.com/integrating-the-data-mappers/
     * 
     * @param string $sql Sql statement.
     * @param array $bindings [optional] Input parameters.
     * @return PDOStatement The PDO statement after execution.
     */
    protected function execute($sql, array $bindings = array()) {
        // Prepare sql statement.
        $statement = $this->prepareStatement($sql);

        // Bind input parameters.
        $this->bindInputParameters($statement, $bindings);

        // Execute prepared sql statement.
        $this->executePreparedStatement($statement);

        return $statement;
    }

    /**
     * Prepare and validate an sql statement.<br/>
     * 
     * ---------------------------------------------------------------------------------
     * If the database server cannot successfully prepare the statement,
     * PDO::prepare() returns FALSE or emits PDOException (depending on error handling).
     * ---------------------------------------------------------------------------------
     * 
     * @param string $sql Sql statement.
     * @return PDOStatement If the database server successfully prepares the statement,
     * return a PDOStatement object. Otherwise return FALSE or emit PDOException 
     * (depending on error handling).
     * @throws Php_PDOException
     * @throws PDOException
     */
    private function prepareStatement($sql) {
        try {
            $statement = $this->getConnection()->prepare($sql);
            if (!$statement) {
                throw new PDOException('The sql statement can not be prepared!');
            }
        } catch (Php_PDOException $exc) {
            throw new PDOException('The sql statement can not be prepared!', 0, $exc);
        }
        return $statement;
    }

    /**
     * Bind the input parameters to a prepared PDO statement.
     * 
     * @param PDOStatement $statement PDO statement.
     * @param array $bindings Input parameters.
     * @return $this
     */
    private function bindInputParameters($statement, $bindings) {
        foreach ($bindings as $key => $value) {
            $statement->bindValue(
                    $this->getInputParameterName($key)
                    , $value
                    , $this->getInputParameterDataType($value)
            );
        }
        return $this;
    }

    /**
     * Get the name of an input parameter by its key in the bindings array.
     *  
     * @param int|string $key The key of the input parameter in the bindings array.
     * @return int|string The name of the input parameter.
     */
    private function getInputParameterName($key) {
        return is_int($key) ? ($key + 1) : (':' . ltrim($key, ':'));
    }

    /**
     * Get the PDO::PARAM_* constant, e.g the data type of an input parameter, by its value.
     *  
     * @param mixed $value Value of the input parameter.
     * @return int The PDO::PARAM_* constant.
     */
    private function getInputParameterDataType($value) {
        $dataType = PDO::PARAM_STR;
        if (is_int($value)) {
            $dataType = PDO::PARAM_INT;
        } elseif (is_bool($value)) {
            $dataType = PDO::PARAM_BOOL;
        }
        return $dataType;
    }

    /**
     * Execute a prepared PDO statement.
     * 
     * @param PDOStatement $statement PDO statement.
     * @return $this
     * @throws UnexpectedValueException
     */
    private function executePreparedStatement($statement) {
        if (!$statement->execute()) {
            throw new UnexpectedValueException('The statement can not be executed!');
        }
        return $this;
    }

    /**
     * Get the ID of the last inserted row or of the sequence value.
     * 
     * @param string $sequenceObjectName [optional] Name of the sequence object<br/>
     * from which the ID should be returned.
     * @return string The ID of the last row, or the last value retrieved from the specified<br/>
     * sequence object, or an error IM001 SQLSTATE If the PDO driver does not support this.
     */
    public function getLastInsertId($sequenceObjectName = NULL) {
        return $this->getConnection()->lastInsertId($sequenceObjectName);
    }

    public function getConnection() {
        return $this->connection;
    }

    public function setConnection(PDO $connection) {
        $this->connection = $connection;
        return $this;
    }

    public function getFetchMode() {
        return $this->fetchMode;
    }

    public function setFetchMode($fetchMode) {
        $this->fetchMode = $fetchMode;
        return $this;
    }

    public function getFetchArgument() {
        return $this->fetchArgument;
    }

    public function setFetchArgument($fetchArgument) {
        $this->fetchArgument = $fetchArgument;
        return $this;
    }

    public function getFetchConstructorArguments() {
        return $this->fetchConstructorArguments;
    }

    public function setFetchConstructorArguments($fetchConstructorArguments) {
        $this->fetchConstructorArguments = $fetchConstructorArguments;
        return $this;
    }

    public function getFetchCursorOrientation() {
        return $this->fetchCursorOrientation;
    }

    public function setFetchCursorOrientation($fetchCursorOrientation) {
        $this->fetchCursorOrientation = $fetchCursorOrientation;
        return $this;
    }

    public function getFetchCursorOffset() {
        return $this->fetchCursorOffset;
    }

    public function setFetchCursorOffset($fetchCursorOffset) {
        $this->fetchCursorOffset = $fetchCursorOffset;
        return $this;
    }

}


关于您的第一个问题:对于将类存储在何处没有约定.选择所需的任何文件系统结构.但请确保:


About your first question: There is no convention about where you should store your classes. Choose whatever file sytem structure you wish. But make sure that:

1)您正在使用 PSR中推荐的自动加载器和名称空间-4自动加载标准.

2)您可以随时唯一地标识每个组件类.您可以通过两种方式实现这一目的:通过对每个类(UserControllerUserMapperUserView等)应用相应的后缀,或通过在use语句中定义相应的类别名,例如:

2) You can uniquely identify each component class at any time. You can achieve this in two ways: Either by applying a corresponding suffix to each class (UserController, UserMapper, UserView, etc), or by defining corresponding class aliases in the use statements, like:

namespace App\Controllers;

use App\Models\DomainObjects\User;
use App\Models\Mappers\User as UserMapper;
use App\Models\Repositories\User as UserRepository;

文件系统结构可能类似于以下内容-这是我的项目中使用的结构,如果乍一看太复杂,请抱歉:

The file system structure could be something like the following - it's the one used in my project, so sorry if it's too complex at first sight:

App/Core中:

App/中:

祝你好运!

这篇关于如何将数据库与模型分离以及如何在多个表上扩展模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆