Skip to content

Commit 5d3ab0b

Browse files
committed
refactor
1 parent c08a05f commit 5d3ab0b

8 files changed

Lines changed: 341 additions & 552 deletions

File tree

src/Relations/HasMany.php

Lines changed: 3 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
use CodeIgniter\Entity\Entity;
88
use CodeIgniter\Model;
9-
use Exception;
109
use Michalsn\CodeIgniterRelations\Enums\RelationTypes;
11-
use Michalsn\CodeIgniterRelations\Exceptions\RelationException;
12-
use Michalsn\CodeIgniterRelations\Exceptions\RelationWriteException;
1310
use Michalsn\CodeIgniterRelations\Traits\PerParentLimit;
11+
use Michalsn\CodeIgniterRelations\Traits\SavesDirectMany;
1412

1513
/**
1614
* HasMany Relation
@@ -24,6 +22,7 @@
2422
class HasMany extends Relation
2523
{
2624
use PerParentLimit;
25+
use SavesDirectMany;
2726

2827
/**
2928
* @var RelationTypes The relation type
@@ -81,130 +80,13 @@ public function lazyLoad(array|object $parent): array
8180
return $this->model->findAll();
8281
}
8382

84-
public function save(array|object $data): false|object
83+
protected function setRelationKeys(array|object &$data): void
8584
{
86-
if ($this->contextParentId === null) {
87-
throw RelationException::forMissingParentContext('save()');
88-
}
89-
90-
// Set the foreign key
9185
if (is_array($data)) {
9286
$data[$this->foreignKey] = $this->contextParentId;
9387
} else {
9488
$data->{$this->foreignKey} = $this->contextParentId;
9589
}
96-
97-
$primaryKey = get_model_property($this->model, 'primaryKey');
98-
99-
// Determine if this is an update or insert based on primary key presence
100-
$hasPrimaryKey = is_array($data) ? isset($data[$primaryKey]) : isset($data->{$primaryKey});
101-
102-
if ($hasPrimaryKey) {
103-
// Update existing record
104-
$id = is_array($data) ? $data[$primaryKey] : $data->{$primaryKey};
105-
106-
if (! $this->model->update($id, $data)) {
107-
return false;
108-
}
109-
110-
// Return the updated entity
111-
return $this->model->find($id);
112-
}
113-
114-
// Insert new record
115-
$insertId = $this->model->insert($data, true);
116-
117-
if ($insertId === false) {
118-
return false;
119-
}
120-
121-
// Return the inserted entity
122-
return $this->model->find($insertId);
123-
}
124-
125-
public function saveMany(array $dataSet, bool $useTransaction = true): array
126-
{
127-
if ($this->contextParentId === null) {
128-
throw RelationException::forMissingParentContext('saveMany()');
129-
}
130-
131-
$db = $this->model->db;
132-
$succeededIds = [];
133-
$failedIndexes = [];
134-
$errors = [];
135-
$primaryKey = get_model_property($this->model, 'primaryKey');
136-
$useTransaction = $useTransaction && $db->transDepth === 0; // Don't nest transactions
137-
138-
if ($useTransaction) {
139-
$db->transStart();
140-
}
141-
142-
try {
143-
foreach ($dataSet as $index => $data) {
144-
// Set the foreign key
145-
if (is_array($data)) {
146-
$data[$this->foreignKey] = $this->contextParentId;
147-
} else {
148-
$data->{$this->foreignKey} = $this->contextParentId;
149-
}
150-
151-
$hasPrimaryKey = is_array($data) ? isset($data[$primaryKey]) : isset($data->{$primaryKey});
152-
$entityId = null;
153-
154-
if ($hasPrimaryKey) {
155-
// Update existing
156-
$entityId = is_array($data) ? $data[$primaryKey] : $data->{$primaryKey};
157-
$success = $this->model->update($entityId, $data);
158-
} else {
159-
// Insert new
160-
$entityId = $this->model->insert($data, true);
161-
$success = $entityId !== false;
162-
}
163-
164-
if (! $success) {
165-
$failedIndexes[] = $index;
166-
$errors[$index] = $this->model->errors();
167-
168-
if ($useTransaction) {
169-
// Rollback and throw exception
170-
$db->transRollback();
171-
172-
throw RelationWriteException::forTransactionRollback(
173-
$succeededIds,
174-
$failedIndexes,
175-
$errors,
176-
'save',
177-
$index,
178-
);
179-
}
180-
} else {
181-
$succeededIds[] = $entityId;
182-
}
183-
}
184-
185-
if ($useTransaction) {
186-
$db->transComplete();
187-
}
188-
189-
// If there were failures and no transaction, throw exception with partial results
190-
if ($failedIndexes !== []) {
191-
throw RelationWriteException::forPartialFailure(
192-
$succeededIds,
193-
$failedIndexes,
194-
$errors,
195-
);
196-
}
197-
198-
return $succeededIds;
199-
} catch (RelationWriteException $e) {
200-
throw $e;
201-
} catch (Exception $e) {
202-
if ($useTransaction) {
203-
$db->transRollback();
204-
}
205-
206-
throw $e;
207-
}
20890
}
20991

21092
/**

src/Relations/HasManyThrough.php

Lines changed: 5 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*
2222
* This allows accessing all distantly related models through an intermediate one.
2323
*/
24-
class HasManyThrough extends Relation
24+
class HasManyThrough extends ThroughRelation
2525
{
2626
use PerParentLimit;
2727

@@ -30,62 +30,6 @@ class HasManyThrough extends Relation
3030
*/
3131
public RelationTypes $type = RelationTypes::HasManyThrough;
3232

33-
/**
34-
* The intermediate model
35-
*/
36-
protected Model $throughModel;
37-
38-
/**
39-
* The foreign key on the intermediate table
40-
*/
41-
protected string $firstKey;
42-
43-
/**
44-
* The foreign key on the final table
45-
*/
46-
protected string $secondKey;
47-
48-
/**
49-
* The local key on the parent table
50-
*/
51-
protected string $localKey;
52-
53-
/**
54-
* The local key on the intermediate table
55-
*/
56-
protected string $secondLocalKey;
57-
58-
/**
59-
* Constructor
60-
*
61-
* @param Model $parentModel The parent model instance
62-
* @param string $relatedModelClass The final related model class
63-
* @param string $throughModelClass The intermediate model class
64-
* @param string|null $firstKey Foreign key on intermediate table
65-
* @param string|null $secondKey Foreign key on final table
66-
* @param string|null $localKey Local key on parent table
67-
* @param string|null $secondLocalKey Local key on intermediate table
68-
*/
69-
public function __construct(
70-
Model $parentModel,
71-
string $relatedModelClass,
72-
string $throughModelClass,
73-
?string $firstKey = null,
74-
?string $secondKey = null,
75-
?string $localKey = null,
76-
?string $secondLocalKey = null,
77-
) {
78-
$this->parentModel = $parentModel;
79-
$this->model = model($relatedModelClass);
80-
$this->throughModel = model($throughModelClass);
81-
$this->localKey = $localKey ?? get_model_property($parentModel, 'primaryKey');
82-
$this->firstKey = $firstKey ?? get_foreign_key($parentModel);
83-
$this->secondLocalKey = $secondLocalKey ?? get_model_property($this->throughModel, 'primaryKey');
84-
$this->secondKey = $secondKey ?? get_foreign_key($this->throughModel);
85-
$this->primaryKey = $this->localKey;
86-
$this->foreignKey = $this->secondKey; // For compatibility with base class
87-
}
88-
8933
public function eagerLoad(array $results, string $returnType, string $relationName): array
9034
{
9135
if ($results === []) {
@@ -100,26 +44,11 @@ public function eagerLoad(array $results, string $returnType, string $relationNa
10044
}
10145

10246
// Build the through query with joins
103-
$throughTable = get_model_property($this->throughModel, 'table');
104-
$finalTable = get_model_property($this->model, 'table');
105-
106-
// Start with the final model
107-
$this->model
108-
->select("{$finalTable}.*")
109-
->join(
110-
$throughTable,
111-
"{$throughTable}.{$this->secondLocalKey} = {$finalTable}.{$this->secondKey}",
112-
'inner',
113-
)
114-
->whereIn("{$throughTable}.{$this->firstKey}", $ids);
115-
116-
// Apply custom query callback if provided
117-
if ($this->queryCallback !== null) {
118-
($this->queryCallback)($this->model);
119-
}
47+
$this->buildThroughJoinQuery($ids);
12048

12149
if ($this->hasLimitInQuery()) {
12250
// Add partition column to SELECT for window function (with DB prefix)
51+
$throughTable = get_model_property($this->throughModel, 'table');
12352
$prefixedThroughTable = $this->model->db->prefixTable($throughTable);
12453
$this->model->select("{$prefixedThroughTable}.{$this->firstKey}", false);
12554

@@ -151,23 +80,8 @@ public function lazyLoad(array|object $parent): array
15180
{
15281
$id = $this->getPrimaryKeyValue($parent);
15382

154-
$throughTable = get_model_property($this->throughModel, 'table');
155-
$finalTable = get_model_property($this->model, 'table');
156-
157-
// Build query with joins
158-
$this->model
159-
->select("{$finalTable}.*")
160-
->join(
161-
$throughTable,
162-
"{$throughTable}.{$this->secondLocalKey} = {$finalTable}.{$this->secondKey}",
163-
'inner',
164-
)
165-
->where("{$throughTable}.{$this->firstKey}", $id);
166-
167-
// Apply custom query callback if provided
168-
if ($this->queryCallback !== null) {
169-
($this->queryCallback)($this->model);
170-
}
83+
// Build the through query with joins
84+
$this->buildLazyThroughQuery($id);
17185

17286
return $this->model->findAll();
17387
}

0 commit comments

Comments
 (0)