Friends, again it's time the author's column of the corporate blog PG Day'17 . We offer you a comparative analysis of the work with PostgreSQL from the popular ORM from varanio .
ORM (Object-Relational Mapping), in theory, should save us from writing SQL queries and, ideally, abstracting the database (the way data is stored) in general, so that we can work with classes that more or less express business objects. -logic, without asking the question, in which tables all this in fact lies.
Let's see how it works modern libraries in PHP. Let's look at some typical cases and compare ORM with bare, hand-written SQL.
For example, we take two tables: books and authors of books, many-to-many relations (books can have many authors, authors can have many books). Those. in the database it will be books, authors and the author_book link table:
CREATE TABLE authors (
id bigserial,
name varchar(1000) not null,
primary key(id)
);
CREATE TABLE books (
id bigserial,
name VARCHAR (1000) not null,
text text not null,
PRIMARY KEY (id)
);
CREATE TABLE author_book (
author_id bigint REFERENCES authors(id),
book_id bigint REFERENCES books(id),
PRIMARY key(author_id, book_id)
);
.
.
, :
$stmt = $pdo->prepare(
"INSERT INTO books (name, text) VALUES (:name, :text) RETURNING id"
);
$stmt->execute(
[':name' => '', ':text' => '']
);
$bookId = $stmt->fetchColumn();
$stmt = $pdo->prepare(
"INSERT INTO authors (name) VALUES (:name) RETURNING id"
);
$stmt->execute(
[':name' => '']
);
$authorId = $stmt->fetchColumn();
$pdo->prepare(
"INSERT INTO author_book (book_id, author_id) VALUES (:book_id, :author_id)"
)->execute(
[':book_id' => $bookId, ':author_id' => $authorId]
);
. PDO, - , - . SQL.
Laravel ORM Eloquent. Eloquent — , , ActiveRecord, .. (""), .
, . , . $timestamps = false, .
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
public $timestamps = false;
public function authors()
{
return $this->belongsToMany(Author::class);
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Author extends Model
{
public $timestamps = false;
public function books()
{
return $this->belongsToMany(Books::class);
}
}
, many-to-many . :
$book = new \App\Book;
$book->name = '';
$book->text = '';
$book->save();
$author = new \App\Author;
$author->name = '';
$author->save();
//
$book->authors()->save($author);
:
$book = \App\Book::create(['name' => '', 'text' => '']);
$author = \App\Author::create(['name' => '']);
$book->authors()->save($author);
, , , SQL, .
DataMapper. , - . (Repository), .. , Repository, EntityManager.
:
bin/console doctrine:mapping:import --force AppBundle yml
bin/console doctrine:generate:entities AppBundle
yml- , , (, many-to-many) .. .
, yml , . , yml, . , DDD.
- , .. POJO (plain old php object):
namespace AppBundle\Entity;
/**
* Authors
*/
class Authors
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @var
\Doctrine\Common\Collections\Collection
*/
private $book;
/**
* Constructor
*/
public function __construct()
{
$this->book = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Authors
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Add book
*
* @param \AppBundle\Entity\Books $book
*
* @return Authors
*/
public function addBook(\AppBundle\Entity\Books $book)
{
$this->book[] = $book;
return $this;
}
/**
* Remove book
*
* @param \AppBundle\Entity\Books $book
*/
public function removeBook(\AppBundle\Entity\Books $book)
{
$this->book->removeElement($book);
}
/**
* Get book
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getBook()
{
return $this->book;
}
}
namespace AppBundle\Entity;
/**
* Books
*/
class Books
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $text;
/**
* @var \Doctrine\Common\Collections\Collection
*/
private $author;
/**
* Constructor
*/
public function __construct()
{
$this->author = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Books
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set text
*
* @param string $text
*
* @return Books
*/
public function setText($text)
{
$this->text = $text;
return $this;
}
/**
* Get text
*
* @return string
*/
public function getText()
{
return $this->text;
}
/**
* Add author
*
* @param \AppBundle\Entity\Authors $author
*
* @return Books
*/
public function addAuthor(\AppBundle\Entity\Authors $author)
{
$this->author[] = $author;
return $this;
}
/**
* Remove author
*
* @param \AppBundle\Entity\Authors $author
*/
public function removeAuthor(\AppBundle\Entity\Authors $author)
{
$this->author->removeElement($author);
}
/**
* Get author
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getAuthor()
{
return $this->author;
}
}
. :
$em = $this->getDoctrine()->getManager();
$author = new Authors();
$author->setName("");
$book = new Books();
$book->setName("");
$book->setText("");
$book->addAuthor($author);
$author->addBook($book);
$em->persist($book);
$em->persist($author);
$em->flush();
, ORM , SQL. setName .. , SQL-. .
$stmt = $pdo->prepare('UPDATE books SET name=:name WHERE id=:id');
$stmt->execute([
':name' => ' 2', ':id' => 1
]);
$book = \App\Book::find(1);
$book->name = ' 2';
$book->save();
$em = $this->getDoctrine()->getManager();
$repository = $em->getRepository(Books::class);
$book = $repository->find(1);
$book->setName(" 2");
$em->persist($book);
- ORM, SQL.
:
delete from author_book;
delete from books;
delete from authors;
insert into authors
(id, name)
values
(1, ' 1'),
(2, ' 2'),
(3, ' 3');
insert into books
(id, name, text)
values
(1, ' 1', ' 1'),
(2, ' 2', ' 2'),
(3, ' 3', ' 3');
insert into author_book
(author_id , book_id)
values
(1,1),
(1,2),
(2,2),
(3,3);
SQL , (, json):
select
b.id as book_id,
b.name as book_name,
json_agg(a.name) as authors
from books b
inner join author_book ab
on b.id = ab.book_id
INNER join authors a
on ab.author_id = a.id
GROUP BY
b.id
:
book_id | book_name | authors
---------+-----------+------------------------
1 | 1 | [" 1"]
3 | 3 | [" 3"]
2 | 2 | [" 1", " 2"]
(3 rows)
- "Getting Started":
$books = \App\Book::all();
/** @var $author \App\Author */
foreach ($books as $book) {
print $book->name . "\n";
foreach ($book->authors as $author) {
print $author->name . ";";
}
}
, SQL. , works like magic. . Eloquent 4 :
select * from "books";
-- :
select
"authors".*,
"author_book"."book_id" as "pivot_book_id",
"author_book"."author_id" as "pivot_author_id"
from "authors"
inner join "author_book"
on "authors"."id" = "author_book"."author_id"
where "author_book"."book_id" = ?
. , .
-, select *
select authors.*
. . "" (" " " "), , . , , .. , . , authors.* .
? -, , , . (['id', 'name'])
. , with() .. "eager loading". :
$books = \App\Book::with('authors')->get(['id', 'name']);
, :
select "id", "name" from "books";
select
"authors".*,
"author_book"."book_id" as "pivot_book_id",
"author_book"."author_id" as "pivot_author_id"
from "authors"
inner join "author_book"
on "authors"."id" = "author_book"."author_id"
where
"author_book"."book_id" in (?, ?, ?);
: authors . , in() id, , , PostgreSQL. , , , . , , , .
, ORM Query Builder:
$result = DB::table('books')
->join('author_book', 'books.id', '=', 'author_book.book_id')
->join('authors', 'author_book.author_id', '=', 'authors.id')
->select('books.id', 'books.name', 'authors.name')
->get();
, , ORM. SQL, , .
-:
$doctrine = $this->getDoctrine();
$books = $doctrine
->getRepository(Books::class)
->findAll();
foreach ($books as $book) {
print $book->getName() . "\n";
foreach ($book->getAuthor() as $author) {
print $author->getName() . ";";
}
}
Laravel. SQL-, , :
SELECT
t0.id AS id_1,
t0.name AS name_2,
t0.text AS text_3
FROM books t0;
-- 3 :
SELECT
t0.id AS id_1,
t0.name AS name_2
FROM authors t0
INNER JOIN author_book
ON t0.id = author_book.author_id
WHERE
author_book.book_id = ?
.. , , , .
findAll .., , , - - . , SQL- DQL, , . , .
$query = $this->getDoctrine()->getManager()->createQuery('
SELECT
partial b.{id, name}, partial a.{id, name}
FROM AppBundle\Entity\Books b
JOIN b.author a'
);
$books = $query->getResult();
, , , , :
SELECT
b0_.id AS id_0,
b0_.name AS name_1,
a1_.id AS id_2,
a1_.name AS name_3
FROM
books b0_
INNER JOIN author_book a2_
ON b0_.id = a2_.book_id
INNER JOIN authors a1_
ON a1_.id = a2_.author_id
, SQL . , ORM- , .
DQL SQL, , , .
, "".
, .
UPDATE authors
SET name = ''
WHERE id in (
SELECT id
FROM authors
ORDER BY id DESC
LIMIT 2
);
:
\App\Author::orderBy('id', 'desc')->take(2)->update(["name" => ""]);
, . , , .
, SO, :
\App\Author::whereIn(
'id',
function($query) {
$query->select('id')
->from((new \App\Author())->getTable())
->orderBy('id', 'desc')
->limit(2);
}
)->update(['name' => '']);
, . query builder - .
, DQL , .
, , query builder, - , . ORM , . , - update .
- , .
, - . CRUD- ORM . , — SQL. , / (, CTE). , .
ORM vs SQL .
Source: https://habr.com/ru/post/328690/
All Articles