Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.72% covered (warning)
48.72%
38 / 78
50.00% covered (warning)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
BelongsTo
48.72% covered (warning)
48.72%
38 / 78
50.00% covered (warning)
50.00%
2 / 4
200.21
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
 getChild
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParent
80.00% covered (success)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 getEagerRelationships
44.29% covered (warning)
44.29%
31 / 70
0.00% covered (danger)
0.00%
0 / 1
197.20
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 "belongs to" 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 BelongsTo extends AbstractRelationship
30{
31
32    /**
33     * Child record
34     * @var ?Record
35     */
36    protected ?Record $child = null;
37
38    /**
39     * Constructor
40     *
41     * Instantiate the relationship object
42     *
43     * @param Record $child
44     * @param string $foreignTable
45     * @param string $foreignKey
46     * @param ?array $options
47     */
48    public function __construct(Record $child, string $foreignTable, string $foreignKey, ?array $options = null)
49    {
50        parent::__construct($foreignTable, $foreignKey, $options);
51        $this->child = $child;
52    }
53
54    /**
55     * Get child record
56     *
57     * @return ?Record
58     */
59    public function getChild(): ?Record
60    {
61        return $this->child;
62    }
63
64    /**
65     * Get parent
66     *
67     * @param  ?array $options
68     * @return ?Record
69     */
70    public function getParent(?array $options = null): ?Record
71    {
72        $table  = $this->foreignTable;
73        $values = $this->child[$this->foreignKey];
74
75        if (!empty($this->children)) {
76            return $table::with($this->children)->getById($values);
77        } else {
78            return $table::findById($values, $options);
79        }
80    }
81
82    /**
83     * Get eager relationships
84     *
85     * @param  array $ids
86     * @throws Exception
87     * @return array
88     */
89    public function getEagerRelationships(array $ids): array
90    {
91        if (($this->foreignTable === null) || ($this->foreignKey === null)) {
92            throw new Exception('Error: The foreign table and key values have not been set.');
93        }
94
95        $results = [];
96        $table   = $this->foreignTable;
97        $db      = $table::db();
98        $sql     = $db->createSql();
99        $columns = null;
100
101        if (!empty($this->options)) {
102            if (isset($this->options['select'])) {
103                $columns = $this->options['select'];
104            }
105        }
106
107        $foreignKey  = $this->foreignKey;
108        $primaryKeys = (new $table())->getPrimaryKeys();
109        $info        = $table::getTableInfo();
110
111        if (!in_array($foreignKey, $info['columns']) && (count($primaryKeys) == 1)) {
112            $foreignKey = $primaryKeys[0];
113        }
114
115        $placeholders = array_fill(0, count($ids), $sql->getPlaceholder());
116        $sql->select($columns)->from($table::table())->where->in($foreignKey, $placeholders);
117
118        if (!empty($this->options)) {
119            if (isset($this->options['limit'])) {
120                $sql->select()->limit((int)$this->options['limit']);
121            }
122
123            if (isset($this->options['offset'])) {
124                $sql->select()->offset((int)$this->options['offset']);
125            }
126            if (isset($this->options['join'])) {
127                $joins = (is_array($this->options['join']) && isset($this->options['join']['table'])) ?
128                    [$this->options['join']] : $this->options['join'];
129
130                foreach ($joins as $join) {
131                    if (isset($join['type']) && method_exists($sql->select(), $join['type'])) {
132                        $joinMethod = $join['type'];
133                        $sql->select()->{$joinMethod}($join['table'], $join['columns']);
134                    } else {
135                        $sql->select()->leftJoin($join['table'], $join['columns']);
136                    }
137                }
138            }
139            if (isset($this->options['order'])) {
140                if (!is_array($this->options['order'])) {
141                    $orders = (str_contains($this->options['order'], ',')) ?
142                        explode(',', $this->options['order']) : [$this->options['order']];
143                } else {
144                    $orders = $this->options['order'];
145                }
146                foreach ($orders as $order) {
147                    $ord = Parser\Order::parse(trim($order));
148                    $sql->select()->orderBy($ord['by'], $db->escape($ord['order']));
149                }
150            }
151        }
152
153        $db->prepare($sql)
154            ->bindParams($ids)
155            ->execute();
156
157        $rows               = $db->fetchAll();
158        $parentIds          = [];
159        $childRelationships = [];
160
161        $primaryKey = (new $table())->getPrimaryKeys();
162        $primaryKey = (count($primaryKey) == 1) ? reset($primaryKey) : $this->foreignKey;
163
164        foreach ($rows as $row) {
165            $parentIds[] = $row[$primaryKey];
166            $record = new $table();
167            $record->setColumns($row);
168            $results[$row[$this->foreignKey]] = $record;
169        }
170        if (!empty($this->children) && !empty($parentIds)) {
171            foreach ($results as $record) {
172                $record->getWithRelationships();
173                foreach ($record->getRelationships() as $relationship) {
174                    $childRelationships = $relationship->getEagerRelationships($parentIds);
175                }
176            }
177        }
178
179        if (!empty($childRelationships)) {
180            $children    = $this->children;
181            $subChildren = null;
182            if (str_contains($children, '.')) {
183                $names       = explode('.', $children);
184                $children    = array_shift($names);
185                $subChildren = implode('.', $names);
186            }
187
188            foreach ($results as $record) {
189                if (!empty($subChildren)) {
190                    $record->addWith($subChildren);
191                }
192                $rel = (isset($childRelationships[$record[$primaryKey]])) ?
193                    $childRelationships[$record[$primaryKey]] : [];
194
195                $record->setRelationship($children, $rel);
196            }
197        }
198
199        return $results;
200    }
201
202}