1: <?php
2:
3: App::uses('ArrayUtil', 'Base.Lib');
4:
5: class HasManyUtilsBehavior extends ModelBehavior {
6:
7: const SETTING_ASSOCIATIONS = 'associations';
8:
9: 10: 11: 12:
13: private $options;
14:
15: 16: 17: 18:
19: private $toRemoveAssociationIds = array();
20:
21: public function setup(\Model $model, $config = array()) {
22: $this->options[$model->name] = $config;
23: }
24:
25: public function afterFind(\Model $model, $results, $primary = false) {
26: if ($results) {
27: foreach ($model->hasMany as $alias => $association) {
28: foreach (array_keys($results) as $k) {
29: if (!empty($results[$k][$alias]) && is_array($results[$k][$alias]) && in_array($alias, $this->options[$model->name][self::SETTING_ASSOCIATIONS])) {
30: $results[$k][$alias] = $this->_applyAfterFind($model->{$alias}, $results[$k][$alias]);
31: }
32: }
33: }
34: }
35:
36: return $results;
37: }
38:
39: private function _applyAfterFind(\Model $model, $subresults) {
40: foreach ($model->Behaviors->enabled() as $behaviorName) {
41: $behaviorResults = $model->Behaviors->{$behaviorName}->afterFind(
42: $model, $subresults, false
43: );
44: if (is_array($behaviorResults)) {
45: $subresults = $behaviorResults;
46: }
47: }
48:
49: $subresults = $model->afterFind($subresults, false);
50:
51: return $subresults;
52: }
53:
54: public function beforeSaveAll(\Model $model, $options) {
55: $this->_setParentId($model);
56: $this->_saveAllAssociationsToRemove($model);
57: return true;
58: }
59:
60: public function afterSaveAll(\Model $model, $created) {
61: $this->_removeNotAddedAssociationRows($model);
62: return true;
63: }
64:
65: private function _saveAllAssociationsToRemove(\Model $model) {
66: foreach ($this->_associations($model) as $association) {
67: foreach ($this->_savedAssociations($model, $association) as $subId) {
68: if (!$this->_associationRowOnData($model, $association, $subId)) {
69: $this->toRemoveAssociationIds[$model->name][$association][] = $subId;
70: }
71: }
72: }
73: }
74:
75: private function _removeNotAddedAssociationRows(\Model $model) {
76: foreach ($this->_associations($model) as $association) {
77: if (!empty($this->toRemoveAssociationIds[$model->name][$association])) {
78: foreach ($this->toRemoveAssociationIds[$model->name][$association] as $subRowId) {
79: $model->{$association}->delete($subRowId);
80: }
81: }
82: }
83: }
84:
85: private function _associationRowOnData($model, $association, $subrowId) {
86:
87: foreach ($this->_dataAssociationRows($model, $association) as $subRow) {
88: if (!empty($subRow[$model->{$association}->primaryKey]) && ($subRow[$model->{$association}->primaryKey] == $subrowId)) {
89: return true;
90: }
91: }
92:
93: return false;
94: }
95:
96: private function _associations(\Model $model) {
97: return empty($this->options[$model->name][self::SETTING_ASSOCIATIONS]) ? array() : ArrayUtil::arraylize($this->options[$model->name][self::SETTING_ASSOCIATIONS]);
98: }
99:
100: private function _dataAssociationRows(\Model $model, $association) {
101: if (!empty($model->data[$association]) && is_array($model->data[$association])) {
102: return $model->data[$association];
103: } else {
104: return array();
105: }
106: }
107:
108: private function _savedAssociations(\Model $model, $association) {
109: if (empty($model->data[$model->alias][$model->primaryKey])) {
110: return array();
111: }
112:
113: $rows = $model->{$association}->find(
114: 'all', array(
115: 'conditions' => array(
116: $this->_foreignKey($model, $association) => $model->data[$model->alias][$model->primaryKey]
117: ),
118: 'fields' => array(
119: "{$model->{$association}->alias}.{$model->{$association}->primaryKey}"
120: )
121: )
122: );
123:
124: $ids = array();
125:
126: foreach ($rows as $row) {
127: $ids[] = $row[$model->{$association}->alias][$model->{$association}->primaryKey];
128: }
129:
130: return $ids;
131: }
132:
133: private function _foreignKey(\Model $model, $association) {
134: foreach (array($model->hasMany, $model->hasAndBelongsToMany) as $collection) {
135: foreach ($collection as $alias => $associationData) {
136: if ($alias == $association) {
137: return $alias . '.' . $associationData['foreignKey'];
138: }
139: }
140: }
141:
142: return false;
143: }
144:
145: private function _setParentId(\Model $model) {
146: if (empty($model->data[$model->alias][$model->primaryKey])) {
147: $model->data[$model->alias][$model->primaryKey] = $this->_newId($model);
148: }
149: }
150:
151: private function _newId(\Model $model) {
152: $row = $model->find('first', array('fields' => "max({$model->alias}.{$model->primaryKey}) as maxId"));
153: $maxId = empty($row[0]['maxId']) ? 0 : $row[0]['maxId'];
154: return $maxId + 1;
155: }
156:
157: }
158: