Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/ResultSet/RowPrototypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
namespace PhpDb\ResultSet;

/**
* Interface for objects that can serve as row prototypes in ResultSets.
* Interface for objects that can serve as row prototypes in RowPrototypeResultSets.
*
* Row prototypes are cloned for each row and populated via exchangeArray().
* Row prototypes are cloned (but do not have to be) for each row and populated via populate().
* This interface allows custom row objects (like RowGateway) to be used
* as prototypes alongside ArrayObject.
* as prototypes without depending on ArrayObject.
*/
interface RowPrototypeInterface
{
/**
* Exchange the current data for the provided array.
* Populate the prototype with row data. Mutating vs. returning a new instance is up to the implementation.
*/
public function exchangeArray(array $array): array;
public function populate(array $data): RowPrototypeInterface;

/**
* Current data as an array and match current RowGateway implementations.
Expand Down
59 changes: 59 additions & 0 deletions src/ResultSet/RowPrototypeResultSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace PhpDb\ResultSet;

use Override;

use function is_array;

class RowPrototypeResultSet extends AbstractResultSet implements RowPrototypeResultSetInterface
{
public function __construct(
private RowPrototypeInterface $rowPrototype,
) {}

Check failure on line 15 in src/ResultSet/RowPrototypeResultSet.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 15 in src/ResultSet/RowPrototypeResultSet.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

/**
* Iterator: get current item
*/
#[Override]
public function current(): array|RowPrototypeInterface|null
{
$data = parent::current();

if (is_array($data)) {
return (clone $this->getRowPrototype())->populate($data);
}

return $data;
}

/** {@inheritDoc} */
#[Override]
public function getRowPrototype(): RowPrototypeInterface
{
return $this->rowPrototype;
}

/** {@inheritDoc} */
#[Override]
public function setRowPrototype(RowPrototypeInterface $rowPrototype): ResultSetInterface&RowPrototypeResultSetInterface

Check warning on line 41 in src/ResultSet/RowPrototypeResultSet.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Line exceeds 120 characters; contains 123 characters

Check warning on line 41 in src/ResultSet/RowPrototypeResultSet.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Line exceeds 120 characters; contains 123 characters
{
$this->rowPrototype = $rowPrototype;

return $this;
}

/** {@inheritDoc} */
#[Override]
public function toArray(): array
{
$return = [];
foreach ($this as $row) {
$return[] = $row instanceof RowPrototypeInterface ? $row->toArray() : $row;
}

return $return;
}
}
15 changes: 15 additions & 0 deletions src/ResultSet/RowPrototypeResultSetInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpDb\ResultSet;

/**
* Capability interface for a ResultSet whose rows clone a RowPrototypeInterface prototype.
*/
interface RowPrototypeResultSetInterface
{
public function getRowPrototype(): RowPrototypeInterface;

public function setRowPrototype(RowPrototypeInterface $rowPrototype): ResultSetInterface&RowPrototypeResultSetInterface;

Check warning on line 14 in src/ResultSet/RowPrototypeResultSetInterface.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Line exceeds 120 characters; contains 124 characters

Check warning on line 14 in src/ResultSet/RowPrototypeResultSetInterface.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Line exceeds 120 characters; contains 124 characters
}
236 changes: 118 additions & 118 deletions src/RowGateway/AbstractRowGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,52 @@ abstract class AbstractRowGateway implements ArrayAccess, Countable, RowGatewayI

protected ?Feature\FeatureSet $featureSet = null;

#[Override]
#[ReturnTypeWillChange]
public function count(): int
{
return count($this->data);
}

#[Override]
public function delete(): int
{
$this->initialize();

$where = [];
foreach ($this->primaryKeyColumn as $pkColumn) {
$where[$pkColumn] = $this->primaryKeyData[$pkColumn] ?? null;
}

// @todo determine if we need to do a select to ensure 1 row will be affected

$rowsAffected = 0;
$statement = $this->sql->prepareStatementForSqlObject($this->sql->delete()->where($where));
$result = $statement->execute();

$rowsAffected = $result->getAffectedRows();
if ($rowsAffected === 1) {
$this->primaryKeyData = null;
}

return $rowsAffected;
}

/**
* docs: Behaviour has changed - this no longer returns RowGatewayInterface but
* instead an array of the old data as per original PHP spec.
*
* @return array<string, mixed>
*/
public function exchangeArray(array $array): array
{
$oldData = $this->data;

$this->populate($array, true);

return $oldData;
}

/**
* initialize()
*/
Expand Down Expand Up @@ -63,10 +109,62 @@ public function initialize(): void
$this->isInitialized = true;
}

/**
* Offset Exists
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetExists($offset): bool
{
return array_key_exists($offset, $this->data);
}

/**
* Offset get
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetGet($offset): mixed
{
return $this->data[$offset];
}

/**
* Offset set
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetSet($offset, mixed $value): static
{
$this->data[$offset] = $value;

return $this;
}

/**
* Offset unset
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetUnset($offset): static
{
$this->data[$offset] = null;

return $this;
}

/**
* Populate Data
*/
public function populate(array $rowData, bool $rowExistsInDatabase = false): RowGatewayInterface
public function populate(array $rowData, bool $rowExistsInDatabase = false): static
{
$this->initialize();

Expand All @@ -80,19 +178,9 @@ public function populate(array $rowData, bool $rowExistsInDatabase = false): Row
return $this;
}

/**
* docs: Behaviour has changed - this no longer returns RowGatewayInterface but
* instead an array of the old data as per original PHP spec.
*
* @return array<string, mixed>
*/
public function exchangeArray(array $array): array
public function rowExistsInDatabase(): bool
{
$oldData = $this->data;

$this->populate($array, true);

return $oldData;
return $this->primaryKeyData !== null;
}

#[Override]
Expand Down Expand Up @@ -158,92 +246,25 @@ public function save(): int
return $rowsAffected;
}

#[Override]
public function delete(): int
{
$this->initialize();

$where = [];
foreach ($this->primaryKeyColumn as $pkColumn) {
$where[$pkColumn] = $this->primaryKeyData[$pkColumn] ?? null;
}

// @todo determine if we need to do a select to ensure 1 row will be affected

$rowsAffected = 0;
$statement = $this->sql->prepareStatementForSqlObject($this->sql->delete()->where($where));
$result = $statement->execute();

$rowsAffected = $result->getAffectedRows();
if ($rowsAffected === 1) {
$this->primaryKeyData = null;
}

return $rowsAffected;
}

/**
* Offset Exists
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetExists($offset): bool
{
return array_key_exists($offset, $this->data);
}

/**
* Offset get
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetGet($offset): mixed
{
return $this->data[$offset];
}

/**
* Offset set
*
* @param string $offset
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetSet($offset, mixed $value): static
public function toArray(): array
{
$this->data[$offset] = $value;

return $this;
return $this->data;
}

/**
* Offset unset
*
* @param string $offset
* @throws Exception\RuntimeException
*/
#[Override]
#[ReturnTypeWillChange]
public function offsetUnset($offset): static
{
$this->data[$offset] = null;

return $this;
}

#[Override]
#[ReturnTypeWillChange]
public function count(): int
{
return count($this->data);
}

public function toArray(): array
protected function processPrimaryKeyData(): void
{
return $this->data;
$this->primaryKeyData = [];
foreach ($this->primaryKeyColumn as $column) {
if (! isset($this->data[$column])) {
throw new Exception\RuntimeException(
'While processing primary key data, a known key ' . $column . ' was not found in the data array',
);
}
$this->primaryKeyData[$column] = $this->data[$column];
}
}

/**
Expand All @@ -257,39 +278,18 @@ public function __get(string $name): mixed
throw new Exception\InvalidArgumentException('Not a valid column in this row: ' . $name);
}

public function __set(string $name, mixed $value): void
{
$this->offsetSet($name, $value);
}

public function __isset(string $name): bool
{
return $this->offsetExists($name);
}

public function __unset(string $name): void
{
$this->offsetUnset($name);
}

public function rowExistsInDatabase(): bool
public function __set(string $name, mixed $value): void
{
return $this->primaryKeyData !== null;
$this->offsetSet($name, $value);
}

/**
* @throws Exception\RuntimeException
*/
protected function processPrimaryKeyData(): void
public function __unset(string $name): void
{
$this->primaryKeyData = [];
foreach ($this->primaryKeyColumn as $column) {
if (! isset($this->data[$column])) {
throw new Exception\RuntimeException(
'While processing primary key data, a known key ' . $column . ' was not found in the data array'
);
}
$this->primaryKeyData[$column] = $this->data[$column];
}
$this->offsetUnset($name);
}
}
Loading
Loading