1: <?php
2:
3: namespace PHPixie\ORM\Relationships\Type\OneTo;
4:
5: abstract class Handler extends \PHPixie\ORM\Relationships\Relationship\Implementation\Handler
6: implements \PHPixie\ORM\Relationships\Relationship\Handler\Mapping\Database,
7: \PHPixie\ORM\Relationships\Relationship\Handler\Preloading,
8: \PHPixie\ORM\Relationships\Relationship\Handler\Cascading\Delete
9: {
10:
11: public function query($side, $related)
12: {
13: $config = $side->config();
14: if ($side->type() !== 'owner') {
15: $model = $config->itemModel;
16: $property = $config->itemOwnerProperty;
17: } else {
18: $model = $config->ownerModel;
19: $property = $config->ownerProperty();
20: }
21:
22: $repository = $this->getRepository($model);
23: return $repository->query()->relatedTo($property, $related);
24: }
25:
26: public function linkPlan($config, $owner, $items)
27: {
28: $ownerRepository = $this->getRepository($config->ownerModel);
29: $ownerQuery = $ownerRepository->databaseSelectQuery();
30:
31: $itemRepository = $this->getRepository($config->itemModel);
32: $updateQuery = $itemRepository->databaseUpdateQuery();
33:
34: $queryStep = $this->steps->query($updateQuery);
35: $plan = $this->plans->query($queryStep);
36: $requiredPlan = $plan->requiredPlan();
37:
38: $this->planners->in()->items(
39: $ownerQuery,
40: $config->ownerModel,
41: $owner,
42: $requiredPlan
43: );
44:
45: $this->planners->in()->items(
46: $updateQuery,
47: $config->itemModel,
48: $items,
49: $requiredPlan
50: );
51:
52: $this->planners->update()->subquery(
53: $updateQuery,
54: array(
55: $config->ownerKey => $this->getIdField($ownerRepository)
56: ),
57: $ownerQuery,
58: $requiredPlan
59: );
60:
61: return $plan;
62: }
63:
64: protected function getUnlinkPlan($config, $constrainOwners, $owners, $constrainItems, $items, $logic = 'and')
65: {
66: $itemRepository = $this->getRepository($config->itemModel);
67: $updateQuery = $itemRepository->databaseUpdateQuery();
68:
69: $queryStep = $this->steps->query($updateQuery);
70: $plan = $this->plans->query($queryStep);
71: $requiredPlan = $plan->requiredPlan();
72:
73: $updateQuery->set($config->ownerKey, null);
74:
75: if ($constrainItems) {
76: $this->planners->in()->items(
77: $updateQuery,
78: $config->itemModel,
79: $items,
80: $requiredPlan
81: );
82: }
83:
84: if ($constrainOwners) {
85: $this->planners->in()->itemIds(
86: $updateQuery,
87: $config->ownerKey,
88: $this->getRepository($config->ownerModel),
89: $owners,
90: $requiredPlan,
91: $logic
92: );
93: }
94:
95:
96: return $plan;
97: }
98:
99: public function mapDatabaseQuery($query, $side, $collectionCondition, $plan)
100: {
101: $config = $side->config();
102: $itemRepository = $this->getRepository($config->itemModel);
103: $ownerRepository = $this->getRepository($config->ownerModel);
104:
105: if ($side->type() === 'owner') {
106: $subqueryRepository = $ownerRepository;
107: $queryField = $config->ownerKey;
108: $subqueryField = $this->getIdField($ownerRepository);
109: } else {
110: $subqueryRepository = $itemRepository;
111: $queryField = $this->getIdField($ownerRepository);
112: $subqueryField = $config->ownerKey;
113: }
114:
115: $subquery = $subqueryRepository->databaseSelectQuery();
116:
117: $conditions = $collectionCondition->conditions();
118: $hasConditions = !empty($conditions);
119:
120: $isOwner = $side->type() === 'owner';
121:
122: if(!$isOwner) {
123: $subquery->whereNot($config->ownerKey, null);
124: }
125:
126: if($hasConditions) {
127:
128: if(!$isOwner) {
129: $subquery->startGroup();
130: }
131:
132: $this->mappers->conditions()->map(
133: $subquery,
134: $subqueryRepository->modelName(),
135: $collectionCondition->conditions(),
136: $plan
137: );
138:
139: if(!$isOwner) {
140: $subquery->endGroup();
141: }
142: }
143:
144: if($isOwner) {
145: $query->startConditionGroup(
146: $collectionCondition->logic(),
147: $collectionCondition->isNegated()
148: );
149:
150: $query->whereNot($config->ownerKey, null);
151: $subqueryLogic = 'and';
152: $negateSubquery = false;
153: }else{
154: $subqueryLogic = $collectionCondition->logic();
155: $negateSubquery = $collectionCondition->isNegated();
156: }
157:
158: $this->planners->in()->subquery(
159: $query,
160: $queryField,
161: $subquery,
162: $subqueryField,
163: $plan,
164: $subqueryLogic,
165: $negateSubquery
166: );
167:
168: if($isOwner) {
169: $query->endGroup();
170: }
171: }
172:
173: public function handleDelete($side, $reusableResult, $plan, $sidePath)
174: {
175: $config = $side->config();
176: $itemKey = $config->ownerKey;
177: $itemModel = $config->itemModel;
178:
179: $itemRepository = $this->getRepository($itemModel);
180: $ownerRepository = $this->getRepository($config->ownerModel);
181:
182: $hasHandledSides = false;
183:
184: if ($config->onDelete === 'update') {
185: $query = $itemRepository->databaseUpdateQuery();
186: $query->set($config->ownerKey, null);
187: } else {
188: $deleteMapper = $this->mappers->cascadeDelete();
189: $hasHandledSides = $deleteMapper->isModelHandled($itemModel);
190:
191: if($hasHandledSides) {
192: $query = $itemRepository->databaseSelectQuery();
193: }else{
194: $query = $itemRepository->databaseDeleteQuery();
195: }
196: }
197:
198: $this->planners->in()->result(
199: $query,
200: $config->ownerKey,
201: $reusableResult,
202: $this->getIdField($ownerRepository),
203: $plan
204: );
205:
206: if($hasHandledSides) {
207: $this->mappers->cascadeDelete()->handleQuery($query, $itemModel, $plan, $sidePath);
208:
209: }else{
210: $step = $this->steps->query($query);
211: $plan->add($step);
212: }
213: }
214:
215: public function mapPreload($side, $preloadProperty, $reusableResult, $plan)
216: {
217: $config = $side->config();
218:
219: $itemRepository = $this->getRepository($config->itemModel);
220: $ownerRepository = $this->getRepository($config->ownerModel);
221:
222:
223: if ($side->type() === 'owner') {
224: $preloadRepository = $ownerRepository;
225: $queryField = $this->getIdField($ownerRepository);
226: $resultField = $config->ownerKey;
227: } else {
228: $preloadRepository = $itemRepository;
229: $queryField = $config->ownerKey;
230: $resultField = $this->getIdField($ownerRepository);
231: }
232:
233: $query = $preloadRepository->databaseSelectQuery();
234: $this->planners->in()->result(
235: $query,
236: $queryField,
237: $reusableResult,
238: $resultField,
239: $plan
240: );
241:
242: $preloadStep = $this->steps->reusableResult($query);
243: $plan->add($preloadStep);
244: $loader = $this->loaders->reusableResult($preloadRepository, $preloadStep);
245: $preloadingProxy = $this->loaders->preloadingProxy($loader);
246: $cachingProxy = $this->loaders->cachingProxy($preloadingProxy);
247:
248: $this->mappers->preload()->map(
249: $preloadingProxy,
250: $preloadRepository->modelName(),
251: $preloadProperty->preload(),
252: $preloadStep,
253: $plan
254: );
255:
256: return $this->relationship->preloader(
257: $side,
258: $preloadRepository->config(),
259: $preloadStep,
260: $cachingProxy
261: );
262: }
263:
264: protected function getRepository($modelName)
265: {
266: return $this->models->database()->repository($modelName);
267: }
268:
269: protected function getIdField($repository)
270: {
271: return $repository->config()->idField;
272: }
273:
274: protected function loadSingleProperty($side, $related)
275: {
276: return $this->query($side, $related)->findOne();
277: }
278:
279: }
280: