|
|
@@ -39,6 +39,7 @@ use Cake\ORM\Rule\IsUnique;
|
|
|
use Cake\Utility\Inflector;
|
|
|
use Cake\Validation\ValidatorAwareInterface;
|
|
|
use Cake\Validation\ValidatorAwareTrait;
|
|
|
+use Exception;
|
|
|
use InvalidArgumentException;
|
|
|
use RuntimeException;
|
|
|
|
|
|
@@ -2231,8 +2232,43 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc
|
|
|
*/
|
|
|
public function saveMany($entities, $options = [])
|
|
|
{
|
|
|
+ try {
|
|
|
+ return $this->_saveMany($entities, $options);
|
|
|
+ } catch (PersistenceFailedException $exception) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Persists multiple entities of a table.
|
|
|
+ *
|
|
|
+ * The records will be saved in a transaction which will be rolled back if
|
|
|
+ * any one of the records fails to save due to failed validation or database
|
|
|
+ * error.
|
|
|
+ *
|
|
|
+ * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save.
|
|
|
+ * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity.
|
|
|
+ * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface Entities list.
|
|
|
+ * @throws \Exception
|
|
|
+ * @throws \Cake\ORM\Exception\PersistenceFailedException If an entity couldn't be saved.
|
|
|
+ */
|
|
|
+ public function saveManyOrFail($entities, $options = [])
|
|
|
+ {
|
|
|
+ return $this->_saveMany($entities, $options);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to save.
|
|
|
+ * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity.
|
|
|
+ * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface Entities list.
|
|
|
+ * @throws \Cake\ORM\Exception\PersistenceFailedException If an entity couldn't be saved.
|
|
|
+ */
|
|
|
+ protected function _saveMany($entities, $options = [])
|
|
|
+ {
|
|
|
+ /** @var bool[] $isNew */
|
|
|
$isNew = [];
|
|
|
$cleanup = function ($entities) use (&$isNew) {
|
|
|
+ /** @var \Cake\Datasource\EntityInterface[] $entities */
|
|
|
foreach ($entities as $key => $entity) {
|
|
|
if (isset($isNew[$key]) && $isNew[$key]) {
|
|
|
$entity->unsetProperty($this->getPrimaryKey());
|
|
|
@@ -2242,25 +2278,25 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc
|
|
|
};
|
|
|
|
|
|
try {
|
|
|
- $return = $this->getConnection()
|
|
|
+ $failed = $this->getConnection()
|
|
|
->transactional(function () use ($entities, $options, &$isNew) {
|
|
|
foreach ($entities as $key => $entity) {
|
|
|
$isNew[$key] = $entity->isNew();
|
|
|
if ($this->save($entity, $options) === false) {
|
|
|
- return false;
|
|
|
+ return $entity;
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
- } catch (\Exception $e) {
|
|
|
+ } catch (Exception $e) {
|
|
|
$cleanup($entities);
|
|
|
|
|
|
throw $e;
|
|
|
}
|
|
|
|
|
|
- if ($return === false) {
|
|
|
+ if ($failed !== null) {
|
|
|
$cleanup($entities);
|
|
|
|
|
|
- return false;
|
|
|
+ throw new PersistenceFailedException($failed, ['saveMany']);
|
|
|
}
|
|
|
|
|
|
return $entities;
|
|
|
@@ -2336,6 +2372,91 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * Deletes multiple entities of a table.
|
|
|
+ *
|
|
|
+ * The records will be deleted in a transaction which will be rolled back if
|
|
|
+ * any one of the records fails to delete due to failed validation or database
|
|
|
+ * error.
|
|
|
+ *
|
|
|
+ * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to delete.
|
|
|
+ * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity.
|
|
|
+ * @return bool|\Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface
|
|
|
+ * False on failure, entities list on success.
|
|
|
+ * @throws \Exception
|
|
|
+ * @see \Cake\ORM\Table::delete() for options and events related to this method.
|
|
|
+ */
|
|
|
+ public function deleteMany($entities, $options = [])
|
|
|
+ {
|
|
|
+ $failed = $this->_deleteMany($entities, $options);
|
|
|
+
|
|
|
+ if ($failed !== null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $entities;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Deletes multiple entities of a table.
|
|
|
+ *
|
|
|
+ * The records will be deleted in a transaction which will be rolled back if
|
|
|
+ * any one of the records fails to delete due to failed validation or database
|
|
|
+ * error.
|
|
|
+ *
|
|
|
+ * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to delete.
|
|
|
+ * @param array|\ArrayAccess $options Options used when calling Table::save() for each entity.
|
|
|
+ * @return \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface Entities list.
|
|
|
+ * @throws \Exception
|
|
|
+ * @throws \Cake\ORM\Exception\PersistenceFailedException
|
|
|
+ * @see \Cake\ORM\Table::delete() for options and events related to this method.
|
|
|
+ */
|
|
|
+ public function deleteManyOrFail($entities, $options = [])
|
|
|
+ {
|
|
|
+ $failed = $this->_deleteMany($entities, $options);
|
|
|
+
|
|
|
+ if ($failed !== null) {
|
|
|
+ throw new PersistenceFailedException($failed, ['deleteMany']);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $entities;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param \Cake\Datasource\EntityInterface[]|\Cake\Datasource\ResultSetInterface $entities Entities to delete.
|
|
|
+ * @param array|\ArrayAccess $options Options used.
|
|
|
+ * @return \Cake\Datasource\EntityInterface|null
|
|
|
+ */
|
|
|
+ protected function _deleteMany($entities, $options = [])
|
|
|
+ {
|
|
|
+ $options = new ArrayObject((array)$options + [
|
|
|
+ 'atomic' => true,
|
|
|
+ 'checkRules' => true,
|
|
|
+ '_primary' => true,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $failed = $this->_executeTransaction(function () use ($entities, $options) {
|
|
|
+ foreach ($entities as $entity) {
|
|
|
+ if (!$this->_processDelete($entity, $options)) {
|
|
|
+ return $entity;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }, $options['atomic']);
|
|
|
+
|
|
|
+ if ($failed === null && $this->_transactionCommitted($options['atomic'], $options['_primary'])) {
|
|
|
+ foreach ($entities as $entity) {
|
|
|
+ $this->dispatchEvent('Model.afterDeleteCommit', [
|
|
|
+ 'entity' => $entity,
|
|
|
+ 'options' => $options,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $failed;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Perform the delete operation.
|
|
|
*
|
|
|
* Will delete the entity provided. Will remove rows from any
|