mattsmithdev / pdo-crud-for-free-repositories
使使用 PDO 进行数据库 CRUD 操作变得非常简单
Requires
- php: ^7.2.5|^8.0
- symfony/dotenv: ^5.0
README
注意 - 这实际上是 pdo-crud-for-free 包的另一种方法
此包提供了一些类,旨在以简单的方式为使用 PDO(与 MySQL 一起)的程序员提供一些实例 CRUD(创建-读取-更新-删除)方法,'免费'地仅通过创建 Mattsmithdev\PdoCrudRepo\DatabaseTableRepository 的实体仓库子类即可。
所有代码都遵循 PSR-1、PSR-12 编码标准。类遵循 PSR-4 自动加载标准。
使用此库的示例项目
有一个示例项目展示了如何使用此库
安装
通过 Composer
$ composer require mattsmithdev/pdo-crud-for-free-repositories
用法
此示例假设您有一个名为 'movie' 的 MySQL 数据库表,其中包含列 'id' 和 'title'。您需要编写相应的类 'Movie'(请注意首字母大写 - 因为这是一个 PHP 类)。此外,您还需要编写一个仓库类,在您的 PHP 类和相应的表之间工作,在此示例中,仓库类命名为 'MovieRepository'
// file: /src/Movie.php namespace <MyNameSpace>; class Movie { // private properties with EXACTLY same names as DB table columns private $id; private $title; public function getId() { return $this->id; } public function getTitle() { return $this->title; } }
// file: /src/MovieRepository.php namespace <MyNameSpace>; use Mattsmithdev\PdoCrudRepo\DatabaseTableRepository; class MovieRepository extends DatabaseTableRepository { // no methods needed if you've followed defaults // all the 'magic' is done through relfection ... }
// file: /public-web/index.php or /src/SomeController->method() require_once __DIR__ . '/<PATH_TO_AUTLOAD>'; // create a repository object use <MyNameSpace>\MovieRepository; $movieRepository = new MovieRepository(); // get all records from DB as an array of Dvd objects $movies = $movieRepository->findAll(); // output each Dvd object as HTML lines in the form 'title = Jaws II' foreach($movies as $movie){ /** * @var $movie <MyNameSpace>\Movie */ print 'id = ' . $movie->getId(); print '<br>'; print 'title = ' . $movie->getTitle(); print '<br>'; }
最后,您需要在文件 .env
中定义您的数据库连接凭据,如下所示
MYSQL_USER=root MYSQL_PASSWORD=passpass MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_DATABASE=evote
如果尚未存在,将创建命名的数据库模式...
更多详细信息请见下文。此外,在 GitGub 上有一个完整的示例网络应用程序项目:pdo-crud-for-free-repositories-example-project
更详细的用法说明(以及重要假设)
假设 1:lowerCamelCase - 数据库表列名与 PHP 类属性匹配
此工具假设您的数据库表列名以及它们对应的 PHP 私有类属性以 'lowerCamelCase' 一致命名,例如。
id
title
category
price
vatRate
firstName
aLongVariableNameOfSeveralWords
假设 2:PHP 类没有构造函数。
由于 PDO 将数据库行转换为对象实例时填充对象的属性,因此您的 DB 表对应的 PHP 类不需要构造函数
因此,您会创建一个新对象,并使用对象的公共 'setter' 方法,例如。
$m = new Movie(); $m->setTitle('Jaws'); $m->setPrice(9.99); etc.
假设 3:每个类都有一个整数 id
属性
每个实体类都应该有一个整数 id
属性。此属性应在数据库表模式中是 AUTO_INCREMENT
主键,例如。
-- SQL statement to create the table -- create table if not exists movie ( id integer primary key AUTO_INCREMENT, title text, price float );
注意:请不要将此命名为其他名称,例如 idMovie
或 movieId
或 ID
等。 - 只需简单的 id
假设 4:数据库表名是单数形式且全部小写
此工具假设您的数据库表名是单数形式,全部 小写。例如。
-
表名:
movie
- 实体类名:
Movie.php
- 实体类名:
-
表名:
moviecategory
- 实体类名:
MovieCategory.php
- 实体类名:
-
表名:
alongtablename
- 实体类名:
ALongTableName
- 实体类名:
步骤 1:创建您的数据库表。
您需要一个数据库模式
- 如果不存在,将创建一个新的模式
对于每个实体类,您需要一个相应的数据库表(具有整数 'id' 字段,主键,自增)
- 您可以在您的数据库管理工具中创建这些表
- 您可以使用仓库方法
createTable()
- 可以从您的实体属性中尝试推断列数据类型,或者使用您提供的自己的SQL
步骤 2:创建相应的PHP(实体)类
例如:
<?php namespace Whatever; class Movie { private int $id; private string $title; private string $category; private float $price; // and public getters and setters ...
`
步骤 3:创建一个将数据库表映射到您的PHP实体类的仓库类(这是一个从 Mattsmithdev\PdoCrudRepo\DatabaseTableRepository
继承的子类)
例如:创建仓库类 MovieRepository,将表 movie
映射到PHP类 Evote\Movie
<?php namespace Whatever; // same as for Entity calss use Mattsmithdev\PdoCrudRepo\DatabaseManager; use Mattsmithdev\PdoCrudRepo\DatabaseTableRepository; class MovieRepository extends DatabaseTableRepository { }
注意 - 我个人觉得添加一个创建新对象并将其插入数据库的方法很方便 - 例如:
<?php namespace Tudublin; use Mattsmithdev\PdoCrudRepo\DatabaseTableRepository; class MovieRepository extends DatabaseTableRepository { public function createAndInsert($title, $price, $category): void { $m = new Movie(); $m->setTitle($title); $m->setPrice($price); $m->setCategory($category); $this->insert($m); } }
步骤 4:在文件 .env
中定义您的MySQL数据库凭据
在文件 .env
中如下定义您的数据库连接凭据
MYSQL_USER=root MYSQL_PASSWORD=passpass MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_DATABASE=evote
步骤 5:现在使用“神奇出现”的DB CRUD方法。
例如,要从表 'movie' 获取所有电影记录的数组,只需编写
$movieRepository = new MovieRepository(); $movies = $movieRepository->getAll();
注意:可以在创建仓库类时传递可选参数以覆盖默认值
-
如果仓库在另一个命名空间中,请传递命名空间名称
$params = [ 'namespace' => 'DifferentNameSpace' ]; $repo = new MovieRepository($params);
-
类名 - 如果不是
Repository
前面的名称$params = [ 'className' => 'differentClassName' ]; $repo = new MovieRepository($params);
-
表名不是实体类仓库的小写版本
$params = [ 'tableName' => 'differentTableName' ]; $repo = new MovieRepository($params);
->findAll()
此方法返回对应数据库表每行的所有对象的数组,例如:
// array of Movie objects, populated from database table 'movie' $movieRepository = new MovieRepository(); $movies = $movieRepository->findAll();
->find($id)
此方法返回对应数据库表记录的给定 'id' 的一个对象(如果不存在具有该主键id的记录,则返回 'null')
// one Movie object (or 'null'), populated by row in database table 'movie' with id=27 $movieRepository = new MovieRepository(); $movie = $movieRepository->find(27);
->delete($id)
此方法删除与给定 'id' 相关的记录,根据删除的成功与否返回 true/false
// delete row in database table 'movie' with id=12 $movieRepository = new MovieRepository(); $deleteSuccess = $movieRepository->delete(12);
->deleteAll()
此方法删除关联数据库表的所有记录
// delete all rows in database table 'movie' $movieRepository = new MovieRepository(); $deleteSuccess = $movieRepository->deleteAll();
->insert($movie)
此方法基于提供的对象的内容(此对象中的任何 'id' 都会被忽略,因为表是自增的,因此它留给数据库分配新的唯一 'id' 给新记录)返回新记录的 'id'(或 -1 如果插入时出错)
// create new object $movie = new Movie(); $movie->setTitle('Jaws II'); $movie->setCategory('thriller'); $movie->setPrice(9.99); // attempt to inset row in database table 'movie' - auto assign new unique `id` $movieRepository = new MovieRepository(); $id = $movieRepository->insert($movie); // decision based on success/failure of insert if ($id < 0){ // error action } else { // success action }
->update($movie)
此方法基于提供的对象的内容添加或更新数据库中的现有行,根据删除的成功与否返回 true/false
例如:
// update DB record for object 'movie' $movieRepository = new MovieRepository(); $updateSuccess = $movieRepository->update($movie);
->searchByColumn($columnName, $searchText))
对给定列执行带有给定搜索文本的SQL '%' 通配符搜索,返回匹配SQL 'LIKE' 查询的对象数组
例如:
// get all Movies with 'jaws' in the title $movieRepository = new MovieRepository(); $jawsMovies = $movieRepository->searchByColumn('title', 'jaws');
->dropTable()
删除关联的数据库表及其所有数据
例如:
// drop table `movie` from DB $movieRepository = new MovieRepository(); $movieRepository->dropTable();
->createTable()
(方法 1) 如果没有提供SQL参数,则代码会在关联的实体类中查找常量 CREATE_TABLE_SQL,并执行该SQL。 (方法 2) 如果没有找到此类常量,则仓库类将尝试根据实体类属性的数据类型推断数据库列类型。
方法 2 - 自动DB列类型推断
具有如下类型属性的类不需要您提供任何SQL即可使方法正常工作
class Movie { private int $id; private string $title; private float $price; private string $category;
方法 1 - 声明创建表的常量SQL
以下是一个示例,其中类明确声明了一个常量 CREATE_TABLE_SQL,包含您要使用的表创建SQL
class Movie { const CREATE_TABLE_SQL = <<<HERE CREATE TABLE IF NOT EXISTS movie ( id integer PRIMARY KEY AUTO_INCREMENT, title text, price float, category text ) HERE; ... rest of class ...
->createTable($sql)
与上面相同,但创建表的SQL可以作为字符串参数传递给方法
->resetTable( $sql = null )
运行删除/创建/删除所有序列
$this->dropTable(); $this->createTable($sql); // pass through any SQL provided $this->deleteAll();
提供的任何SQL作为参数传递给 createTable(...)
。
然后在我们的迁移代码(例如)中,我们可以删除旧表并创建新表,如下所示
$movieRepository = new MovieRepository(); $movieRespository->resetTable();
自定义PDO方法
如果“免费”的DB方法不足,可以很容易地为您自己的PHP类添加对应于您的数据库表的方法。
以下是一个可以添加到类 Product 的方法,允许通过 'id' 和 'description' 中的文本进行自定义搜索
<?php namespace Whatever; // same as for Entity calss use Mattsmithdev\PdoCrudRepo\DatabaseManager; use Mattsmithdev\PdoCrudRepo\DatabaseTableRepository; class ProductRepository extends DatabaseTableRepository { /** * illustrate custom PDO DB method * in this case we search for products with an id >= $minId, and whose descrption contains $searchText * * @param $minId * @param $searchText * * @return array */ public function getAllAboveMinIdContainsString($minId, $searchText) { $db = new DatabaseManager(); $connection = $db->getDbh(); // wrap wildcard '%' around the search text for the SQL query $searchText = '%' . $searchText . '%'; $sql = 'SELECT * FROM product WHERE (description LIKE :searchText) AND (id > :minId)'; $statement = $connection->prepare($sql); $statement->bindParam(':minId', $minId, \PDO::PARAM_INT); $statement->bindParam(':searchText', $searchText, \PDO::PARAM_STR); $statement->setFetchMode(\PDO::FETCH_CLASS, $this->getClassNameForDbRecords()); $statement->execute(); $products = $statement->fetchAll(); return $products; }
下面是一个使用示例,在一个控制器函数中
// get products from DB as array of Product objects - id > minId, description containing $searchText $minId = 2; $searchText = 'er'; $productRepository = new ProductRepository(); $products = $productRepository->getAllAboveMinIdContainsString($minId, $searchText); // outputs something like: // [5] pliers // [7] hammer foreach ($products as $product){ print '<p>'; print 'id [' . $product->getId() . '] '; print $product->getDescription(); } // [1] nut -- not listed due to search criteria
迁移和固定数据
这里有一些简单脚本的示例,用于更新表架构和插入一些初始数据。
使用 createAndInsert(...)
存储库方法
如果我们已经在我们的存储库类中添加了 createAndInsert(...)
方法,那么重置数据库并插入固定数据可以像这样简单
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Tudublin\MovieRepository; $movieRepository = new MovieRepository(); // (1) drop then re-create table $movieRepository->resetTable(); // (2) create objects $movieRepository->createAndInsert('Jaws', 9.99, 'horror'); $movieRepository->createAndInsert('Jumanji', 7, 'entertainment'); // (3) test objects are there $movies = $movieRespository->findAll(); print '<pre>'; var_dump($movies);
这就是 createAndInsert(...)
方法可能的样子
class MovieRepository extends DatabaseTableRepository { public function createAndInsert($title, $price, $category) { $m = new Movie(); $m->setTitle($title); $m->setPrice($price); $m->setCategory($category); $this->insert($m); } }
使用访问器方法创建对象数据
如果我们没有 createAndInsert(...)
方法,那么我们必须创建每个对象,然后使用 insert(...)
方法将其插入到数据库表中
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Tudublin\Movie; use Tudublin\MovieRepository; $movieRespository = new MovieRepository(); // (1) drop then create table $movieRespository->resetTable(); // (3) create objects $m1 = new Movie(); $m1->setTitle('Jaws'); $m1->setPrice(9.99); $m1->setCategory('horror'); $m2 = new Movie(); $m2->setTitle('Jumanji'); $m2->setPrice(9.99); $m2->setCategory('entertainment'); // (3) insert objects into DB $movieRespository->insert($m1); $movieRespository->insert($m2); // (4) test objects are there $movies = $movieRespository->findAll(); print '<pre>'; var_dump($movies);
输出
<pre>/Users/matt/Documents/github/pdo-crud-for-free-repositories/db/movieMigrationAndFixtures.php:35: array(2) { [0] => class Tudublin\Movie#8 (4) { private $id => string(1) "1" private $title => string(4) "Jaws" private $price => string(4) "9.99" private $category => string(6) "horror" } [1] => class Tudublin\Movie#9 (4) { private $id => string(1) "2" private $title => string(7) "Jumanji" private $price => string(4) "9.99" private $category => string(13) "entertainment" } }
变更日志
请参阅 变更日志 以获取更多最近更改的信息。
测试
$ composer test
贡献
安全性
如果您发现任何安全问题,请通过电子邮件 dr_matt_smith@me.com 而不是使用问题跟踪器来联系。
鸣谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅 许可证文件。