Skip to content

Commit 5f9cf65

Browse files
committed
Added support for converting of Row values (backported from LM 4.x)
1 parent 38f122c commit 5f9cf65

File tree

7 files changed

+442
-11
lines changed

7 files changed

+442
-11
lines changed

src/LeanMapper/DefaultMapper.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*
1919
* @author Vojtěch Kohout
2020
*/
21-
class DefaultMapper implements IMapper
21+
class DefaultMapper implements IRowMapper
2222
{
2323

2424
/** @var string */
@@ -123,6 +123,18 @@ public function getImplicitFilters($entityClass, Caller $caller = null)
123123
}
124124

125125

126+
public function convertToRowData($table, array $values)
127+
{
128+
return $values;
129+
}
130+
131+
132+
public function convertFromRowData($table, array $data)
133+
{
134+
return $data;
135+
}
136+
137+
126138

127139
/**
128140
* Trims namespace part from fully qualified class name

src/LeanMapper/Helpers.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Lean Mapper library (http://www.leanmapper.com)
5+
*
6+
* Copyright (c) 2013 Vojtěch Kohout (aka Tharos)
7+
*
8+
* For the full copyright and license information, please view the file
9+
* license.md that was distributed with this source code.
10+
*/
11+
12+
namespace LeanMapper;
13+
14+
use Dibi\Row as DibiRow;
15+
16+
class Helpers
17+
{
18+
19+
public function __construct()
20+
{
21+
throw new Exception\UtilityClassException('Cannot instantiate utility class ' . get_called_class() . '.');
22+
}
23+
24+
25+
/**
26+
* @param mixed $value
27+
* @return string
28+
*/
29+
public static function getType($value)
30+
{
31+
return is_object($value) ? ('instance of ' . get_class($value)) : gettype($value);
32+
}
33+
34+
35+
/**
36+
* @param string $table
37+
* @param array|DibiRow $row
38+
* @return array
39+
*/
40+
public static function convertDbRow($table, $row, IMapper $mapper)
41+
{
42+
if ($row instanceof DibiRow) {
43+
$row = $row->toArray();
44+
45+
} elseif (!is_array($row)) {
46+
throw new Exception\InvalidArgumentException('DB row must be ' . DibiRow::class . '|array, ' . self::getType($row) . ' given.');
47+
}
48+
49+
if ($mapper instanceof IRowMapper) {
50+
return $mapper->convertToRowData($table, $row);
51+
}
52+
53+
return $row;
54+
}
55+
56+
57+
/**
58+
* @param array<array>|DibiRow[] $rows
59+
* @return array<array>
60+
*/
61+
public static function convertDbRows($table, array $rows, IMapper $mapper)
62+
{
63+
$result = [];
64+
65+
foreach ($rows as $k => $row) {
66+
$result[$k] = self::convertDbRow($table, $row, $mapper);
67+
}
68+
69+
return $result;
70+
}
71+
72+
73+
public static function convertRowData($table, array $rowData, IMapper $mapper)
74+
{
75+
if ($mapper instanceof IRowMapper) {
76+
return $mapper->convertFromRowData($table, $rowData);
77+
}
78+
79+
return $rowData;
80+
}
81+
82+
}

src/LeanMapper/IRowMapper.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Lean Mapper library (http://www.leanmapper.com)
5+
*
6+
* Copyright (c) 2013 Vojtěch Kohout (aka Tharos)
7+
*
8+
* For the full copyright and license information, please view the file
9+
* license.md that was distributed with this source code.
10+
*/
11+
12+
namespace LeanMapper;
13+
14+
interface IRowMapper extends IMapper
15+
{
16+
17+
/**
18+
* @param string $table
19+
* @return array
20+
*/
21+
public function convertToRowData($table, array $values);
22+
23+
24+
25+
/**
26+
* @param string $table
27+
* @return array
28+
*/
29+
public function convertFromRowData($table, array $data);
30+
31+
}

src/LeanMapper/Repository.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ protected function insertIntoDatabase(Entity $entity)
191191
$this->connection->query(
192192
'INSERT INTO %n %v',
193193
$this->getTable(),
194-
$values
194+
Helpers::convertRowData($this->getTable(), $values, $this->mapper)
195195
);
196196
return isset($values[$primaryKey]) ? $values[$primaryKey] : $this->connection->getInsertId();
197197
}
@@ -211,7 +211,7 @@ protected function updateInDatabase(Entity $entity)
211211
return $this->connection->query(
212212
'UPDATE %n SET %a WHERE %n = ?',
213213
$this->getTable(),
214-
$values,
214+
Helpers::convertRowData($this->getTable(), $values, $this->mapper),
215215
$primaryKey,
216216
$this->getIdValue($entity)
217217
);
@@ -324,7 +324,7 @@ protected function createEntity(DibiRow $dibiRow, $entityClass = null, $table =
324324
if ($table === null) {
325325
$table = $this->getTable();
326326
}
327-
$result = Result::createInstance($dibiRow, $table, $this->connection, $this->mapper);
327+
$result = Result::createInstance(new DibiRow(Helpers::convertDbRow($table, $dibiRow, $this->mapper)), $table, $this->connection, $this->mapper);
328328
$primaryKey = $this->mapper->getPrimaryKey($table);
329329

330330
$row = $result->getRow($dibiRow->$primaryKey);
@@ -352,7 +352,7 @@ protected function createEntities(array $rows, $entityClass = null, $table = nul
352352
$table = $this->getTable();
353353
}
354354
$entities = [];
355-
$collection = Result::createInstance($rows, $table, $this->connection, $this->mapper);
355+
$collection = Result::createInstance(Helpers::convertDbRows($table, $rows, $this->mapper), $table, $this->connection, $this->mapper);
356356
$primaryKey = $this->mapper->getPrimaryKey($table);
357357
if ($entityClass !== null) {
358358
foreach ($rows as $dibiRow) {

src/LeanMapper/Result.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ private function getReferencedResult($table, $viaColumn, Filtering $filtering =
808808
->where('%n.%n IN %in', $table, $primaryKey, $ids)
809809
->execute()->setRowClass(null)->fetchAll();
810810
}
811-
$this->referenced[$key] = self::createInstance($data, $table, $this->connection, $this->mapper);
811+
$this->referenced[$key] = self::createInstance(Helpers::convertDbRows($table, $data, $this->mapper), $table, $this->connection, $this->mapper);
812812
}
813813
return $this->referenced[$key];
814814
}
@@ -834,7 +834,7 @@ private function getReferencedResult($table, $viaColumn, Filtering $filtering =
834834

835835
if (!isset($this->referenced[$key])) {
836836
$data = $this->connection->query($args)->setRowClass(null)->fetchAll();
837-
$this->referenced[$key] = self::createInstance($data, $table, $this->connection, $this->mapper);
837+
$this->referenced[$key] = self::createInstance(Helpers::convertDbRows($table, $data, $this->mapper), $table, $this->connection, $this->mapper);
838838
}
839839
return $this->referenced[$key];
840840
}
@@ -882,7 +882,7 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt
882882
$statement->where('%n.%n IN %in', $table, $viaColumn, $ids);
883883
}
884884
$data = $statement->execute()->setRowClass(null)->fetchAll();
885-
$this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper);
885+
$this->referencing[$key] = self::createInstance(Helpers::convertDbRows($table, $data, $this->mapper), $table, $this->connection, $this->mapper);
886886
}
887887
} else {
888888
isset($ids) or $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table));
@@ -906,7 +906,7 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt
906906

907907
if (!isset($this->referencing[$key])) {
908908
$data = $this->connection->query($args)->setRowClass(null)->fetchAll();
909-
$this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper);
909+
$this->referencing[$key] = self::createInstance(Helpers::convertDbRows($table, $data, $this->mapper), $table, $this->connection, $this->mapper);
910910
}
911911
}
912912
return $this->referencing[$key];
@@ -923,7 +923,7 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt
923923
$this->buildUnionStrategySql($ids, $table, $viaColumn)
924924
)->setRowClass(null)->fetchAll();
925925
}
926-
$this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper);
926+
$this->referencing[$key] = self::createInstance(Helpers::convertDbRows($table, $data, $this->mapper), $table, $this->connection, $this->mapper);
927927
}
928928
} else {
929929
isset($ids) or $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table));
@@ -951,7 +951,7 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt
951951
if (!isset($this->referencing[$key])) {
952952
$sql = $this->buildUnionStrategySql($ids, $table, $viaColumn, $filtering);
953953
$data = $this->connection->query($sql)->setRowClass(null)->fetchAll();
954-
$result = self::createInstance($data, $table, $this->connection, $this->mapper);
954+
$result = self::createInstance(Helpers::convertDbRows($table, $data, $this->mapper), $table, $this->connection, $this->mapper);
955955
$this->referencing[$key] = $result;
956956
}
957957
}

tests/LeanMapper/RowMapper.2.phpt

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use LeanMapper\Entity;
6+
use Tester\Assert;
7+
8+
require_once __DIR__ . '/../bootstrap.php';
9+
10+
////////////////////
11+
12+
/**
13+
* @property int $id
14+
* @property string $name
15+
* @property Book[] $books m:belongsToMany
16+
*/
17+
class Author extends Entity
18+
{
19+
}
20+
21+
/**
22+
* @property int $id
23+
* @property BookInfo $info
24+
* @property \DateTime $pubdate
25+
* @property Author $author m:hasOne
26+
*/
27+
class Book extends Entity
28+
{
29+
}
30+
31+
class BookInfo
32+
{
33+
private $name;
34+
private $description;
35+
36+
37+
public function __construct(string $name, ?string $description)
38+
{
39+
$this->name = $name;
40+
$this->description = $description;
41+
}
42+
43+
44+
public function getName(): string
45+
{
46+
return $this->name;
47+
}
48+
49+
50+
public function getDescription(): ?string
51+
{
52+
return $this->description;
53+
}
54+
}
55+
56+
class AuthorRepository extends \LeanMapper\Repository
57+
{
58+
/**
59+
* @param $id
60+
* @return Book
61+
* @throws Exception
62+
*/
63+
public function find($id)
64+
{
65+
$row = $this->connection->select('*')->from($this->getTable())->where('id = %i', $id)->fetch();
66+
if ($row === false) {
67+
throw new \Exception('Entity was not found.');
68+
}
69+
return $this->createEntity($row);
70+
}
71+
}
72+
73+
class BookRepository extends \LeanMapper\Repository
74+
{
75+
/**
76+
* @param $id
77+
* @return Book
78+
* @throws Exception
79+
*/
80+
public function find($id)
81+
{
82+
$row = $this->connection->select('*')->from($this->getTable())->where('id = %i', $id)->fetch();
83+
if ($row === false) {
84+
throw new \Exception('Entity was not found.');
85+
}
86+
return $this->createEntity($row);
87+
}
88+
}
89+
90+
class CustomMapper extends TestMapper
91+
{
92+
public function convertToRowData($table, array $values)
93+
{
94+
if ($table === 'book') {
95+
$values['info'] = new BookInfo($values['name'], $values['description']);
96+
unset($values['name']);
97+
unset($values['description']);
98+
}
99+
return $values;
100+
}
101+
102+
103+
public function convertFromRowData($table, array $data)
104+
{
105+
if ($table === 'book' && array_key_exists('info', $data)) {
106+
$data['name'] = $data['info']->getName();
107+
$data['description'] = $data['info']->getDescription();
108+
unset($data['info']);
109+
}
110+
return $data;
111+
}
112+
}
113+
114+
////////////////////
115+
116+
$mapper = new CustomMapper(null);
117+
$authorRepository = new AuthorRepository($connection, $mapper, $entityFactory);
118+
$bookRepository = new BookRepository($connection, $mapper, $entityFactory);
119+
120+
//// detached entity
121+
$book = new Book;
122+
$book->author = $authorRepository->find(1);
123+
$book->pubdate = new \DateTime;
124+
125+
Assert::exception(function () use ($book) {
126+
$book->info;
127+
}, LeanMapper\Exception\Exception::class, 'Cannot get value of property \'info\' in entity Book due to low-level failure: Missing \'info\' column in row with id -1.');
128+
129+
$book->info = new BookInfo('Name', 'description');
130+
Assert::same('Name', $book->info->getName());
131+
Assert::same('description', $book->info->getDescription());
132+
133+
$bookRepository->persist($book);
134+
$bookInfo = $bookRepository->find($book->id)->info;
135+
Assert::same('Name', $bookInfo->getName());
136+
Assert::same('description', $bookInfo->getDescription());
137+
138+
$book->info = new BookInfo('Name 2', null);
139+
$bookRepository->persist($book);
140+
$bookInfo = $bookRepository->find($book->id)->info;
141+
Assert::same('Name 2', $bookInfo->getName());
142+
Assert::null($bookInfo->getDescription());
143+
144+
//// attached entity with NULL
145+
$book = $bookRepository->find(1);
146+
Assert::same($book->info, $book->info);
147+
Assert::same('The Pragmatic Programmer', $book->info->getName());
148+
Assert::null($book->info->getDescription());
149+
150+
//// attached entity with BookInfo
151+
$book = $bookRepository->find(2);
152+
Assert::same('The Art of Computer Programming', $book->info->getName());
153+
Assert::same('very old book about programming', $book->info->getDescription());
154+
$book->pubdate = new \DateTime;
155+
$bookRepository->persist($book);

0 commit comments

Comments
 (0)