Documentation

HasMany extends AbstractRelationship
in package

Relationship class for "has many" relationships

Tags
category

Pop

author

Nick Sagona, III nick@popphp.org

copyright

Copyright (c) 2009-2026 Nick Sagona, III

license

https://www.popphp.org/license New BSD License

version
7.0.0

Table of Contents

Constants

COMPOSITE_KEY_DELIMITER  = "\x1f"
Delimiter used to join multiple column values into one composite lookup key

Properties

$children  : array<string|int, mixed>
Relationship children (list of dotted child paths to eager-load under this relationship)
$foreignKey  : string|array<string|int, mixed>|null
Foreign key
$foreignTable  : string|null
Foreign table class
$options  : array<string|int, mixed>|null
Relationship options
$parent  : Record|null
Parent record

Methods

__construct()  : mixed
Constructor
buildCompositeKey()  : string
Build a single composite lookup key from an ordered list of column values
getChildRelationships()  : array<string|int, mixed>
Get child relationships
getChildren()  : Collection
Get children
getEagerRelationships()  : array<string|int, mixed>
Get eager relationships
getEmptyRelationshipValue()  : mixed
Get the value to use when no eager-loaded result exists for a given leaf record
getForeignKey()  : string|array<string|int, mixed>|null
Get foreign key
getForeignTable()  : string|null
Get foreign table class
getOptions()  : array<string|int, mixed>|null
Get options
getParent()  : Record|null
Get parent record
setChildRelationships()  : static
Set children child relationships
tupleFor()  : array<string|int, mixed>|null
Build an ordered tuple of values for the given columns from a record (either a Record instance or a plain array row). Returns null if any one of the columns is missing or null, i.e. the record has no usable composite key and should be skipped.
applyEagerIdFilter()  : void
Apply the eager-load id filter to a SELECT's WHERE clause and collect its bound values.
assertKeyCardinality()  : void
Validate that an array foreign-key column count matches the target table's own primary-key column count. A plain string $foreignKey is always treated as cardinality 1.
assertTupleCardinality()  : void
Validate that the tuples in an eager-load id list have the same number of components as the array foreign key they will be bound to. A plain string $foreignKey (cardinality 1) and an empty $ids list are always valid.
bindPlaceholder()  : string
Bind a single value as a query parameter and return the placeholder token that must be rendered in its place.
bindPlaceholders()  : array<string|int, mixed>
Bind a flat list of values for a single column and return their placeholder tokens
hydrateChildRelationships()  : void
Hydrate nested child relationships onto a flat list of leaf records, resolving each named child relationship once (accumulated by name) and distributing every one of them onto every leaf record — so multiple differently-named children under this relationship don't overwrite each other.
applyAdditionalColumnsFilter()  : void
Apply the options['columns'] additional WHERE filter (if any) and collect its bound values
applyQueryOptions()  : void
Apply the limit, offset, join, and order options (if any)
hydrateRows()  : array<string|int, mixed>
Hydrate the fetched rows into a per-parent-key map of Collections (or raw arrays), and eager-load any nested child relationships on the resulting leaf records

Constants

COMPOSITE_KEY_DELIMITER

Delimiter used to join multiple column values into one composite lookup key

public string COMPOSITE_KEY_DELIMITER = "\x1f"

Properties

$children

Relationship children (list of dotted child paths to eager-load under this relationship)

protected array<string|int, mixed> $children = []

$foreignKey

Foreign key

protected string|array<string|int, mixed>|null $foreignKey = null

Methods

__construct()

Constructor

public __construct(Record $parent, string $foreignTable, string|array<string|int, mixed> $foreignKey[, array<string|int, mixed>|null $options = null ]) : mixed

Instantiate the relationship object

Parameters
$parent : Record
$foreignTable : string
$foreignKey : string|array<string|int, mixed>
$options : array<string|int, mixed>|null = null

buildCompositeKey()

Build a single composite lookup key from an ordered list of column values

public static buildCompositeKey(array<string|int, mixed> $values) : string
Parameters
$values : array<string|int, mixed>
Return values
string

getChildRelationships()

Get child relationships

public getChildRelationships() : array<string|int, mixed>
Return values
array<string|int, mixed>

getChildren()

Get children

public getChildren([array<string|int, mixed>|null $options = null ]) : Collection
Parameters
$options : array<string|int, mixed>|null = null
Return values
Collection

getEagerRelationships()

Get eager relationships

public getEagerRelationships(array<string|int, mixed> $ids[, bool $toArray = false ]) : array<string|int, mixed>
Parameters
$ids : array<string|int, mixed>
$toArray : bool = false
Tags
throws
Exception
Return values
array<string|int, mixed>

getEmptyRelationshipValue()

Get the value to use when no eager-loaded result exists for a given leaf record

public getEmptyRelationshipValue() : mixed

getForeignKey()

Get foreign key

public getForeignKey() : string|array<string|int, mixed>|null
Return values
string|array<string|int, mixed>|null

getForeignTable()

Get foreign table class

public getForeignTable() : string|null
Return values
string|null

getOptions()

Get options

public getOptions() : array<string|int, mixed>|null
Return values
array<string|int, mixed>|null

setChildRelationships()

Set children child relationships

public setChildRelationships(array<string|int, mixed> $children) : static
Parameters
$children : array<string|int, mixed>
Return values
static

tupleFor()

Build an ordered tuple of values for the given columns from a record (either a Record instance or a plain array row). Returns null if any one of the columns is missing or null, i.e. the record has no usable composite key and should be skipped.

public static tupleFor(mixed $record, array<string|int, mixed> $columns) : array<string|int, mixed>|null
Parameters
$record : mixed
$columns : array<string|int, mixed>
Return values
array<string|int, mixed>|null

applyEagerIdFilter()

Apply the eager-load id filter to a SELECT's WHERE clause and collect its bound values.

protected applyEagerIdFilter(Sql $sql, string|array<string|int, mixed> $key, array<string|int, mixed> $ids, array<string|int, mixed> &$params) : void

A single-column key renders as one "column IN (...)" predicate. A composite key renders as one AND-nested group of per-tuple OR-nested equality groups, so that whatever gets appended to the WHERE clause afterward is ANDed against the whole "matches any of these id tuples" block rather than becoming a sibling OR at the top level. It renders identically when there is no sibling predicate.

Parameters
$sql : Sql
$key : string|array<string|int, mixed>
$ids : array<string|int, mixed>
$params : array<string|int, mixed>

assertKeyCardinality()

Validate that an array foreign-key column count matches the target table's own primary-key column count. A plain string $foreignKey is always treated as cardinality 1.

protected assertKeyCardinality(string|array<string|int, mixed> $foreignKey, array<string|int, mixed> $targetPrimaryKeys) : void
Parameters
$foreignKey : string|array<string|int, mixed>
$targetPrimaryKeys : array<string|int, mixed>
Tags
throws
Exception

assertTupleCardinality()

Validate that the tuples in an eager-load id list have the same number of components as the array foreign key they will be bound to. A plain string $foreignKey (cardinality 1) and an empty $ids list are always valid.

protected assertTupleCardinality(array<string|int, mixed> $ids, string|array<string|int, mixed> $foreignKey) : void
Parameters
$ids : array<string|int, mixed>
$foreignKey : string|array<string|int, mixed>
Tags
throws
Exception

bindPlaceholder()

Bind a single value as a query parameter and return the placeholder token that must be rendered in its place.

protected static bindPlaceholder(Sql $sql, string $column, mixed $value, array<string|int, mixed> &$params) : string

A bare placeholder character is NOT a usable placeholder on every dialect: only MySQL/SQL Server take a bare '?'. PostgreSQL needs a positional '$N' and SQLite/PDO need a named ':name', so the token has to be generated here rather than repeating whatever Sql::getPlaceholder() returns. The Sql object's own parameter counter is used to number (PostgreSQL) and to uniquely name (SQLite/PDO) every parameter in the statement, so callers can bind several groups of parameters into one query without colliding.

The generated names deliberately start with the counter, i.e. with a digit, which is exactly what keeps them from ever colliding with the column-derived names that Sql\Parser\Expression::parseShorthand() emits for the same statement.

Parameters
$sql : Sql
$column : string
$value : mixed
$params : array<string|int, mixed>
Return values
string

bindPlaceholders()

Bind a flat list of values for a single column and return their placeholder tokens

protected static bindPlaceholders(Sql $sql, string $column, array<string|int, mixed> $values, array<string|int, mixed> &$params) : array<string|int, mixed>
Parameters
$sql : Sql
$column : string
$values : array<string|int, mixed>
$params : array<string|int, mixed>
Return values
array<string|int, mixed>

hydrateChildRelationships()

Hydrate nested child relationships onto a flat list of leaf records, resolving each named child relationship once (accumulated by name) and distributing every one of them onto every leaf record — so multiple differently-named children under this relationship don't overwrite each other.

protected hydrateChildRelationships(array<string|int, mixed> $leafRecords, string|array<string|int, mixed> $primaryKeyColumn) : void

The column used to query and match a given child relationship is decided per relationship name, not once for all of them: "to-one by foreign key" children (HasOneOf and BelongsTo) are keyed by the leaf record's own foreign key column — the column holding the value that identifies which foreign row to fetch — while every other kind is keyed by the leaf record's primary key.

Parameters
$leafRecords : array<string|int, mixed>
$primaryKeyColumn : string|array<string|int, mixed>

applyAdditionalColumnsFilter()

Apply the options['columns'] additional WHERE filter (if any) and collect its bound values

private applyAdditionalColumnsFilter(Sql $sql, array<string|int, mixed> &$params) : void

The parsed expressions carry their own dialect-correct placeholder tokens, so the SQL object's parameter counter is advanced by one per bound value to keep any placeholder generated afterward for the same statement in step with them.

Parameters
$sql : Sql
$params : array<string|int, mixed>

hydrateRows()

Hydrate the fetched rows into a per-parent-key map of Collections (or raw arrays), and eager-load any nested child relationships on the resulting leaf records

private hydrateRows(array<string|int, mixed> $rows, string $table, bool|array<string|int, mixed> $toArray) : array<string|int, mixed>
Parameters
$rows : array<string|int, mixed>
$table : string
$toArray : bool|array<string|int, mixed>
Return values
array<string|int, mixed>

        
On this page

Search results