Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
61.54% covered (warning)
61.54%
48 / 78
50.00% covered (warning)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
HasOne
61.54% covered (warning)
61.54%
48 / 78
50.00% covered (warning)
50.00%
2 / 4
109.74
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getParent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getChild
80.00% covered (success)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
5.20
 getEagerRelationships
56.92% covered (warning)
56.92%
37 / 65
0.00% covered (danger)
0.00%
0 / 1
96.22
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Db\Record\Relationships;
15
16use Pop\Db\Record;
17use Pop\Db\Sql\Parser;
18
19/**
20 * Relationship class for "has one" relationships
21 *
22 * @category   Pop
23 * @package    Pop\Db
24 * @author     Nick Sagona, III <dev@nolainteractive.com>
25 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
26 * @license    http://www.popphp.org/license     New BSD License
27 * @version    6.5.0
28 */
29class HasOne extends AbstractRelationship
30{
31
32    /**
33     * Parent record
34     * @var ?Record
35     */
36    protected ?Record $parent = null;
37
38    /**
39     * Constructor
40     *
41     * Instantiate the relationship object
42     *
43     * @param Record $parent
44     * @param string $foreignTable
45     * @param string $foreignKey
46     * @param ?array $options
47     */
48    public function __construct(Record $parent, string $foreignTable, string $foreignKey, ?array $options = null)
49    {
50        parent::__construct($foreignTable, $foreignKey, $options);
51        $this->parent = $parent;
52    }
53
54    /**
55     * Get parent record
56     *
57     * @return ?Record
58     */
59    public function getParent(): ?Record
60    {
61        return $this->parent;
62    }
63
64    /**
65     * Get child
66     *
67     * @param  ?array $options
68     * @return Record
69     */
70    public function getChild(array $options = null): Record
71    {
72        $table  = $this->foreignTable;
73        $values = array_values($this->parent->getPrimaryValues());
74
75        if (count($values) == 1) {
76            $values = $values[0];
77        }
78
79        $columns = [$this->foreignKey => $values];
80
81        if (!empty($options) && !empty($options['columns'])) {
82            $columns = array_merge($columns, $options['columns']);
83        }
84
85        if (!empty($this->children)) {
86            return $table::with($this->children)->getOne($columns, $options);
87        } else {
88            return $table::findOne($columns, $options);
89        }
90    }
91
92    /**
93     * Get eager relationships
94     *
95     * @param  array $ids
96     * @throws Exception
97     * @return array
98     */
99    public function getEagerRelationships(array $ids): array
100    {
101        if (($this->foreignTable === null) || ($this->foreignKey === null)) {
102            throw new Exception('Error: The foreign table and key values have not been set.');
103        }
104
105        $results = [];
106        $table   = $this->foreignTable;
107        $db      = $table::db();
108        $sql     = $db->createSql();
109        $columns = null;
110
111        if (!empty($this->options)) {
112            if (isset($this->options['select'])) {
113                $columns = $this->options['select'];
114            }
115        }
116
117        $placeholders = array_fill(0, count($ids), $sql->getPlaceholder());
118        $sql->select($columns)->from($table::table())->where->in($this->foreignKey, $placeholders);
119
120        if (!empty($this->options)) {
121            if (isset($this->options['limit'])) {
122                $sql->select()->limit((int)$this->options['limit']);
123            }
124
125            if (isset($this->options['offset'])) {
126                $sql->select()->offset((int)$this->options['offset']);
127            }
128            if (isset($this->options['join'])) {
129                $joins = (is_array($this->options['join']) && isset($this->options['join']['table'])) ?
130                    [$this->options['join']] : $this->options['join'];
131
132                foreach ($joins as $join) {
133                    if (isset($join['type']) && method_exists($sql->select(), $join['type'])) {
134                        $joinMethod = $join['type'];
135                        $sql->select()->{$joinMethod}($join['table'], $join['columns']);
136                    } else {
137                        $sql->select()->leftJoin($join['table'], $join['columns']);
138                    }
139                }
140            }
141            if (isset($this->options['order'])) {
142                if (!is_array($this->options['order'])) {
143                    $orders = (str_contains($this->options['order'], ',')) ?
144                        explode(',', $this->options['order']) : [$this->options['order']];
145                } else {
146                    $orders = $this->options['order'];
147                }
148                foreach ($orders as $order) {
149                    $ord = Parser\Order::parse(trim($order));
150                    $sql->select()->orderBy($ord['by'], $db->escape($ord['order']));
151                }
152            }
153        }
154
155        $db->prepare($sql)
156            ->bindParams($ids)
157            ->execute();
158
159        $rows               = $db->fetchAll();
160        $parentIds          = [];
161        $childRelationships = [];
162
163        $primaryKey = (new $table())->getPrimaryKeys();
164        $primaryKey = (count($primaryKey) == 1) ? reset($primaryKey) : $this->foreignKey;
165
166        foreach ($rows as $row) {
167            $parentIds[] = $row[$primaryKey];
168            $record = new $table();
169            $record->setColumns($row);
170            $results[$row[$this->foreignKey]] = $record;
171        }
172
173        if (!empty($this->children) && !empty($parentIds)) {
174            foreach ($results as $record) {
175                $record->getWithRelationships();
176                foreach ($record->getRelationships() as $relationship) {
177                    $childRelationships = $relationship->getEagerRelationships($parentIds);
178                }
179            }
180        }
181
182        if (!empty($childRelationships)) {
183            $children    = $this->children;
184            $subChildren = null;
185            if (str_contains($children, '.')) {
186                $names       = explode('.', $children);
187                $children    = array_shift($names);
188                $subChildren = implode('.', $names);
189            }
190
191            foreach ($results as $record) {
192                if (!empty($subChildren)) {
193                    $record->addWith($subChildren);
194                }
195                $rel = (isset($childRelationships[$record[$primaryKey]])) ?
196                    $childRelationships[$record[$primaryKey]] : [];
197
198                $record->setRelationship($children, $rel);
199            }
200        }
201
202        return $results;
203    }
204
205}