This document is for CakePHP's development version, which can be significantly different
from previous releases.
You may want to read
current stable release documentation instead.
Aplicações geralmente terá algumas maneiras de como os dados são salvos. A
primeira é, obviamente, atravéz de formulários web e a outra é por geração direta
ou alterando dados no código para enviar ao banco de dados.
A maneira mais fácil de inserir dados no banco de dados é criando uma nova entidade
e passando ela pro método save() na classe Table:
useCake\ORM\TableRegistry;// Prior to 3.6 use TableRegistry::get('Articles')$articlesTable=TableRegistry::getTableLocator()->get('Articles');$article=$articlesTable->newEntity();$article->title='A New Article';$article->body='This is the body of the article';if($articlesTable->save($article)){// The $article entity contains the id now$id=$article->id;}
Atualizar seus dados é igualmente fácil, e o método save() também é usado para
esse propósito:
useCake\ORM\TableRegistry;// Prior to 3.6 use TableRegistry::get('Articles')$articlesTable=TableRegistry::getTableLocator()->get('Articles');$article=$articlesTable->get(12);// Return article with id 12$article->title='CakePHP is THE best PHP framework!';$articlesTable->save($article);
CakePHP saberá quando deve realizar uma inserção ou atualização com base no valor
de retorno do método isNew(). Entidades que foram obtidas com get() ou
find() sempre retornará false quando isNew() é chamado nelas.
Por padrão o método save() também salvará associações de um nível:
// Prior to 3.6 use TableRegistry::get('Articles')$articlesTable=TableRegistry::getTableLocator()->get('Articles');$author=$articlesTable->Authors->findByUserName('mark')->first();$article=$articlesTable->newEntity();$article->title='An article by mark';$article->author=$author;if($articlesTable->save($article)){// A chave estrangeira foi atribuída automaticamenteecho$article->author_id;}
O método save() também é capaz de criar novos registros para associações:
$firstComment=$articlesTable->Comments->newEntity();$firstComment->body='The CakePHP features are outstanding';$secondComment=$articlesTable->Comments->newEntity();$secondComment->body='CakePHP performance is terrific!';$tag1=$articlesTable->Tags->findByName('cakephp')->first();$tag2=$articlesTable->Tags->newEntity();$tag2->name='awesome';$article=$articlesTable->get(12);$article->comments=[$firstComment,$secondComment];$article->tags=[$tag1,$tag2];$articlesTable->save($article);
Salvar dados na tabela de ligação é realizado usando a propriedade especial
_joinData. Esta propriedade deve ser um instância de Entity da classe
Table de ligação:
// Link records for the first time.$tag1=$articlesTable->Tags->findByName('cakephp')->first();$tag1->_joinData=$articlesTable->ArticlesTags->newEntity();$tag1->_joinData->tagComment='The CakePHP ORM is so powerful!';$articlesTable->Tags->link($article,[$tag1]);// Update an existing association.$article=$articlesTable->get(1,['contain'=>['Tags']]);$article->tags[0]->_joinData->tagComment='Fresh comment.'// Necessary because we are changing a property directly$article->dirty('tags',true);$articlesTable->save($article,['associated'=>['Tags']]);
Você também pode criar / atualizar informações na tabela de ligação utilizando
newEntity() ou patchEntity(). Os seus dados de POST devem parecer:
$data=['title'=>'My great blog post','body'=>'Some content that goes on for a bit.','tags'=>[['id'=>10,'_joinData'=>['tagComment'=>'Great article!',]],]];$articlesTable->newEntity($data,['associated'=>['Tags']]);
Remover Associação Muitos para Muitos (N para N) Registros¶
A remoção de associação Muitos para Muitos registros é realizada através do método
unlink():
Quando modificando registros, configurando ou alterando diretamente as propriedades,
nenhuma validação é realizada, que é um problema quando está aceitando dados de
formulário. As seções seguintes demostrarão como converter eficientemente dados de
formulário em entidades que podem ser validadas e salva.
Antes de editar e salvar os dados de volta no seu banco de dados, você precisará
converter os dados da requisição, de array mantido na requisição em entidades
que o ORM utiliza. A classe Table fornece uma maneira fácil e eficiente de converter
uma ou várias entidades dos dados de requisição. Você pode converter uma entidade
usando:
//No controller// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');// Valida e converte em um objeto do tipo Entity$entity=$articles->newEntity($this->request->getData());
Note
Se você estiver usando newEntity() e as entidades resultantes estão faltando algum
ou todos os dados passados, verifique se as colunas que deseja definir estão
listadas na propriedade $_accessible da sua entidade. Consulte Atribuição em Massa.
Os dados da requisição devem seguir a estrutura de suas entidades. Por exemplo, se você
tem um artigo, que pertence a um usuário, e tem muitos comentários, os seus dados de
requisição devem ser semelhante:
$data=['title'=>'CakePHP For the Win','body'=>'Baking with CakePHP makes web development fun!','user_id'=>1,'user'=>['username'=>'mark'],'comments'=>[['body'=>'The CakePHP features are outstanding'],['body'=>'CakePHP performance is terrific!'],]];
Por padrão, o método newEntity() valida os dados que são passados para
ele, conforme explicado na seção Validando dados antes de construir entidades. Se você
deseja pular a validação de dados, informe a opção 'validate'=>false:
Ao criar formulários que salvam associações aninhadas, você precisa definir
quais associações devem ser convertidas:
// No controller// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');// Nova entidade com associações aninhadas$entity=$articles->newEntity($this->request->getData(),['associated'=>['Tags','Comments'=>['associated'=>['Users']]]]);
O exemplo acima indica que “Tags”, “Comments” e “Users” para os artigos devem
ser convertidos. Alternativamente, você pode usar a notação de ponto
(dot notation) por brevidade:
// No controller// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');// Nova entidade com associações aninhada usando notação de ponto$entity=$articles->newEntity($this->request->getData(),['associated'=>['Tags','Comments.Users']]);
Você também pode desativar a conversão de possíveis associações aninhadas como:
Os dados associados também são validados por padrão, a menos que seja informado o
contrário. Você também pode alterar o conjunto de validação a ser usada por associação:
// No controller// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');// Pular validação na associação de Tags e// Definino 'signup' como método de validação para Comments.Users$entity=$articles->newEntity($this->request->getData(),['associated'=>['Tags'=>['validate'=>false],'Comments.Users'=>['validate'=>'signup']]]);
O diagrama a seguir fornece uma visão geral do que acontece dentro dos métodos
newEntity() ou patchEntity():
Você sempre pode contar de obter uma entidade de volta com newEntity(). Se a validação
falhar, sua entidade conterá erros, e quaisquer campos inválidos não serão preenchidos
na entidade criada.
Se você está salvando associações belongsToMany, você pode tanto usar uma lista de entidades
ou uma lista de ids. Ao usar uma lista de dados de entidade, seus dados de requisição
devem parecer com:
O exemplo acima criará 2 novas tags. Se você deseja associar um artigo com tags existentes,
você pode usar uma lista de ids. Seus dados de requisição devem parecer com:
Se você precisa associar a alguns belongsToMany registros existentes, e criar novos ao
mesmo tempo, você pode usar um formato expandido:
$data=['title'=>'My title','body'=>'The text','user_id'=>1,'tags'=>[['name'=>'A new tag'],['name'=>'Another new tag'],['id'=>5],['id'=>21]]];
Quando os dados acima são convertidos em entidades, você terá 4 tags. As duas primeiras
serão objetos novos, e as outras duas serão referências a registros existentes.
Ao converter dados belongsToMany, você pode desativar a criação de nova entidade, usando
a opção onlyIds. Quando habilitado, esta opção restringe transformação de
belongsToMany para apenas usar a chave _ids e ignorar todos os outros dados.
Se você deseja atualizar as associações hasMany existentes e atualizar suas
propriedades, primeiro você deve garantir que sua entidade seja carregada com a
associação hasMany. Você pode então usar dados de requisição semelhantes a:
$data=['title'=>'My Title','body'=>'The text','comments'=>[['id'=>1,'comment'=>'Update the first comment'],['id'=>2,'comment'=>'Update the second comment'],['comment'=>'Create a new comment'],]];
Se você está salvando associaçoes hasMany e deseja vincular a registros existentes,
você pode usar o formato _ids:
$data=['title'=>'My new article','body'=>'The text','user_id'=>1,'comments'=>['_ids'=>[1,2,3,4]]];
Ao converter dados hasMany, você pode desativar a criação de nova entidade, usando
a opção onlyIds`.Quandoativada,estaopçãorestringetransformaçãodehasManyparaapenasusarachave``_ids e ignorar todos os outros dados.
Ao criar formulários que cria/atualiza vários registros ao mesmo tempo, você pode usar
o método newEntities():
// No controller.// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$entities=$articles->newEntities($this->request->getData());
Nessa situação, os dados de requisição para vários artigos devem parecer com:
Uma vez que você converteu os dados de requisição em entidades, você pode
salvar com save() e remover com delete():
// No controller.foreach($entitiesas$entity){// Salva a entidade$articles->save($entity);// Remover a entidade$articles->delete($entity);}
O exemplo acima executará uma transação separada para cada entidade salva.
Se você deseja processar todas as entidades como uma única transação, você
pode usar transactional():
// No controller.$articles->getConnection()->transactional(function()use($articles,$entities){foreach($entitiesas$entity){$articles->save($entity,['atomic'=>false]);}});
Também é possível permitir newEntity() escrever em campos não acessiveis.
Por exemplo, id geralmente está ausente da propriedade _accessible.
Nesse caso , você pode usar a opção accessibleFields. Isso pode ser útil para
manter ids de entidades associadas:
// No controller// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$entity=$articles->newEntity($this->request->getData(),['associated'=>['Tags','Comments'=>['associated'=>['Users'=>['accessibleFields'=>['id'=>true]]]]]]);
O exemplo acima manterá a associação inalterada entre Comments e Users para a
entidade envolvida.
Note
Se você estiver usando newEntity() e as entidades resultantes estão faltando algum
ou todos os dados passados, verifique se as colunas que deseja definir estão
listadas na propriedade $_accessible da sua entidade. Consulte Atribuição em Massa.
Para atualizar as entidades, você pode escolher de aplicar dados de requisição diretamente
em uma entidade existente. Isto tem a vantagem que apenas os campos que realmente mudaram
serão salvos, em oposição ao envio de todos os campos para o banco de dados pra ser persistido.
Você pode mesclar um array de dados bruto em uma entidade existente usando o método
patchEntity():
// No controller.// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->get(1);$articles->patchEntity($article,$this->request->getData());$articles->save($article);
Semelhante ao newEntity(), o método patchEntity validará os dados
antes de ser copiado para entidade. O mecanismo é explicado na seção
Validando dados antes de construir entidades. Se você deseja desativar a validação, informe a
o opção validate assim:
// No controller.// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->get(1);$articles->patchEntity($article,$data,['validate'=>false]);
Você também pode alterar a regra de validação utilizada pela entidade ou qualquer
uma das associações:
Como explicado na seção anterior, os dados de requisição deve seguir a
estrutura de sua entidade. O método patchEntity()` é igualmente capaz de
mesclar associações, por padrão, apenas o primeiro nível de associações são
mesclados, mas se você deseja controlar a lista de associações a serem mescladas
ou mesclar em níveis mais profundos, você pode usar o terceiro parâmetro do método:
// No controller.$associated=['Tags','Comments.Users'];$article=$articles->get(1,['contain'=>$associated]);$articles->patchEntity($article,$this->request->getData(),['associated'=>$associated]);$articles->save($article);
As associações são mescladas ao combinar o campo da chave primária nas entidades de origem
com os campos correspondentes no array de dados. As associações irão construir novas
entidades se nenhuma entidade anterior for encontrada para a propriedade alvo da associação.
Por exemplo, forneça alguns dados de requisição como este:
Tentando popular uma entidade sem uma entidade na propriedade user criará
uma nova entidade do tipo user:
// In a controller.$entity=$articles->patchEntity(newArticle,$data);echo$entity->user->username;// Echoes 'mark'
O mesmo pode ser dito sobre associações hasMany e belongsToMany, com uma
advertência importante:
Note
Para as associações belongsToMany, garanta que a entidade relevante tenha
uma propriedade acessível para a entidade associada.
Se um Produto pertence a várias (belongsToMany) Tag:
// Na classe da entidade Productprotectedarray$_accessible=[// .. outras propriedades'tags'=>true,];
Note
Para as associações hasMany e belongsToMany, se houvesse algumas entidades que
que não pudessem ser correspondidas por chave primaria a um registro no array de dados,
então esses registros serão descartados da entidade resultante.
Lembre-se que usando patchEntity() ou patchEntities() não persiste os
dados, isso apenas edita (ou cria) as entidades informadas. Para salvar a entidade você
terá que chamar o método save() da model Table.
Por exemplo, considere o seguinte caso:
$data=['title'=>'My title','body'=>'The text','comments'=>[['body'=>'First comment','id'=>1],['body'=>'Second comment','id'=>2],]];$entity=$articles->newEntity($data);$articles->save($entity);$newData=['comments'=>[['body'=>'Changed comment','id'=>1],['body'=>'A new comment'],]];$articles->patchEntity($entity,$newData);$articles->save($entity);
No final, se a entidade for convertida de volta para um array, você obterá o
seguinte resultado:
['title'=>'My title','body'=>'The text','comments'=>[['body'=>'Changed comment','id'=>1],['body'=>'A new comment'],]];
Como você pode ver, o comentário com id 2 não está mais lá, já que ele não
pode ser correspondido a nada no array $newData. Isso acontece porque CakePHP está
refletindo o novo estado descrito nos dados de requisição.
Algumas vantagens adicionais desta abordagem é que isto reduz o número de
operações a serem executadas ao persistir a entidade novamente.
Por favor, observe que isso não significa que o comentário com id 2 foi removido do
bando de dados, se você deseja remover os comentários para este artigo que não estão
presentes na entidade, você pode coletar as chaves primárias e executar uma exclusão
de lote para esses que não estão na lista:
// Num controller.// Prior to 3.6 use TableRegistry::get('Comments')$comments=TableRegistry::getTableLocator()->get('Comments');$present=(newCollection($entity->comments))->extract('id')->filter()->toArray();$comments->deleteAll(['article_id'=>$article->id,'id NOT IN'=>$present]);
Como você pode ver, isso também ajuda ao criar soluções onde uma associação precisa de
ser implementada como um único conjunto.
Você também pode popular várias entidades ao mesmo tempo. As considerações feitas para
popular (patch) associações hasMany e belongsToMany se aplicam para popular várias entidades:
As comparação são feitas pelo valor do campo da chave primária e as correspondências que
faltam no array das entidades originais serão removidas e não estarão presentes no resultado:
// Num controller.// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$list=$articles->find('popular')->toArray();$patched=$articles->patchEntities($list,$this->request->getData());foreach($patchedas$entity){$articles->save($entity);}
Semelhante de usar patchEntity(), você pode usar o terceiro argumento para
controlar as associações que serão mescladas em cada uma das entidades no array:
// Num controller.$patched=$articles->patchEntities($list,$this->request->getData(),['associated'=>['Tags','Comments.Users']]);
Modificando Dados de Requisição Antes de Contruir Entidades¶
Se você precisa modificar dados de requisição antes de converter em entidades, você
pode usar o evento Model.beforeMarshal. Esse evento deixa você manipular o dados
de requisição antes das entidades serem criadas:
// Inclua as instruções na área superior do seu arquivo.useCake\Event\Event;useArrayObject;// Na classe da sua table ou behaviorpublicfunctionbeforeMarshal(Event$event,ArrayObject$data,ArrayObject$options){if(isset($data['username'])){$data['username']=mb_strtolower($data['username']);}}
O parâmetro $data é uma instância de ArrayObject, então você não precisa
retornar ele para alterar os dados usado para criar entidades.
O propósito principal do beforeMarshal é auxiliar os usuários a passar o processo
de validação quando erros simples podem ser automaticamente resolvidos, ou quando os dados
precisam ser reestruturados para que ele possa ser colocado nos campos corretos.
O evento Model.beforeMarshal é disparado apenas no início do processo de
validação, uma das razões é que o beforeMarshal é permitido de alterar as
regras de validação e opções de salvamento, como o campo whitelist.
Validação é disparada logo após este evento ser finalizado. Um exemplo comum de alteração
de dados antes de ser validado, é retirar espaço no ínicio e final (trimming) de todos os
campos antes de salvar:
// Inclua as instruções na área superior do seu arquivo.useCake\Event\Event;useArrayObject;// Na classe da sua table ou behaviorpublicfunctionbeforeMarshal(Event$event,ArrayObject$data,ArrayObject$options){foreach($dataas$key=>$value){if(is_string($value)){$data[$key]=trim($value);}}}
Por causa de como o processo de marshalling trabalha, se um campo não passar
na validação ele será automaticamente removido do array de dados e não será
copiado na entidade. Isso previne que dados inconsistentes entrem no objeto
de entidade.
Além disso, os dados em beforeMarshal são uma cópia dos dados passados. Isto é
assim porque é importante preservar a entrada original do usuário, pois ele pode
ser usado em outro lugar.
O capítulo Validando dados contém mais informações de como usar os
recursos de validação do CakePHP para garantir que os seus dados permaneçam
corretos e consitentes.
Evitando Ataques de Atribuição em Massa de Propriedade¶
Ao criar ou mesclar entidades a partir de dados de requisição, você precisa ser
cuidadoso com o que você permite seus usuários de alterar ou incluir nas entidades.
Por exemplo, ao enviar um array na requisição contendo o user_id um invasor
pode alterar o proprietário de um artigo, causando efeitos indesejáveis:
Há dois modos de proteger você contra este problema. O primeiro é configurando
as colunas padrão que podem ser definidas com segurança a partir de um requisição
usando o recurso Atribuição em Massa nas entidades.
O segundo modo é usando a opção fieldList ao criar ou mesclar dados em
uma entidade:
// Contem ['user_id' => 100, 'title' => 'Hacked!'];$data=$this->request->getData();// Apenas permite alterar o campo title$entity=$this->patchEntity($entity,$data,['fieldList'=>['title']]);$this->save($entity);
Você também pode controlar quais propriedades poder ser atribuidas para associações:
// Apenas permite alterar o title e tags// e nome da tag é a única columa que pode ser definido$entity=$this->patchEntity($entity,$data,['fieldList'=>['title','tags'],'associated'=>['Tags'=>['fieldList'=>['name']]]]);$this->save($entity);
Usar este recurso é útil quando você tem várias funcões diferentes que seus usuários
podem acessar, e você deseja que eles editem difentes dados baseados em seus
privilégios.
A opção fieldList também é aceita nos métodos newEntity(), newEntities()
e patchEntities().
Ao salvar dados de requisição no seu banco de dados, você primeiro precisa hidratar (hydrate)
uma nova entidade usando newEntity() para passar no save(). Por exemplo:
// Num controller// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->newEntity($this->request->getData());if($articles->save($article)){// ...}
O ORM usa o método isNew() em uma entidade para determinar quando um insert ou update
deve ser realizado ou não. Se o método isNew() retorna true e a entidade tiver um valor
de chave primária, então será emitida uma query “exists”. A query “exists” pode ser suprimida
informando a opção 'checkExisting'=>false no argumento $options:
Uma vez, que você carregou algumas entidades, você provavelmente desejará modificar elas e
atualizar em seu banco de dados. Este é um exercício bem simples no CakePHP:
// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->find('all')->where(['id'=>2])->first();$article->title='My new title';$articles->save($article);
Ao salvar, CakePHP irá aplicar suas regras, e
envolver a operação de salvar em uma trasação de banco de dados. Também atualizará
as propriedades que mudaram. A chamada save() do exemplo acima geraria SQL como:
UPDATEarticlesSETtitle='My new title'WHEREid=2;
Se você tem uma nova entidade, o seguinte SQL seria gerado:
INSERTINTOarticles(title)VALUES('My new title');
Quando uma entidade é salva algumas coisas acontecem:
A verificação de regras será iniciada se não estiver desativada.
A verificação de regras irá disparar o evento Model.beforeRules. Se esse evento for
parado, a operação de salvamento falhará e retornará false.
As regras serão verificadas. Se a entidade está sendo criada, as regras create serão
usadas. Se a entidade estiver sendo atualizada, as regras update serão usadas.
O evento Model.afterRules será disparado.
O evento Model.beforeSave será disparado. Se ele for parado, o processo de
salvamento será abortado, e save() retornará false.
As associações de pais são salvas. Por exemplo, qualquer associação belongsTo listada
serão salvas.
Os campos modificados na entidade serão salvos.
As associações filhas são salvas. Por exemplo, qualquer associação hasMany, hasOne, ou
belongsToMany listada serão salvas.
Se nenhuma alteração é feita na entidade quando ela é salva, os callbacks não
serão disparado porque o salvar não é executado.
O método save() retornará a entidade modificada quando sucesso, e false quando
falhar. Você pode desativar regras e/ou transações usando o argumento $options para salvar:
// Num método de controller ou model$articles->save($article,['checkRules'=>false,'atomic'=>false]);
Quando você está salvando uma entidade, você também pode escolher de salvar alguma ou
todas as entidades associadas. Por padrão, todos as entidades de primeiro nível serão salvas.
Por exemplo salvando um Artigo, você também atualizará todas as entidades modificadas (dirty)
que são diretamente realicionadas a tabela de artigos.
Você pode ajustar as associações que são salvas usando a opção associated:
// Num controller.// Apenas salva a associação de comentários$articles->save($entity,['associated'=>['Comments']]);
Você pode definir para salvar associações distantes ou profundamente aninhadas
usando a notação de pontos (dot notation):
// Salva a company (empresa), employees (funcionários) e os addresses (endereços) relacionadoparacadaumdeles$companies->save($entity,['associated'=>['Employees.Addresses']]);
Além disso, você pode combinar a notação de pontos (dot notation) para associações com
o array de opções:
As suas entidades devem ser estruturadas na mesma maneira como elas são quando carregadas
do banco de dados. Consulte a documentação do form helper para saber como criar inputs
para associações.
Se você está construindo ou modificando dados de associação após a construção de suas entidades,
você terá que marcar a propriedade da associação como modificado com o método dirty():
Ao salvar associações belongsTo, o ORM espera uma única entidade aninhada nomeada com a
singular, underscored versão do nome da associação.
Por exemplo:
// Num controller.$data=['title'=>'First Post','user'=>['id'=>1,'username'=>'mark']];// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->newEntity($data,['associated'=>['Users']]);$articles->save($article);
Ao salvar associações hasOne, o ORM espera uma única entidade aninhada nomeada com a
singular, underscored versão do nome da associação.
Por exemplo:
// Num controller.$data=['id'=>1,'username'=>'cakephp','profile'=>['twitter'=>'@cakephp']];// Prior to 3.6 use TableRegistry::get('Users')$users=TableRegistry::getTableLocator()->get('Users');$user=$users->newEntity($data,['associated'=>['Profiles']]);$users->save($user);
Ao salvar associações hasMany, o ORM espera um array de entidades nomeada com a
plural, underscored versão do nome da associação.
Por exemplo:
// Num controller.$data=['title'=>'First Post','comments'=>[['body'=>'Best post ever'],['body'=>'I really like this.']]];// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->newEntity($data,['associated'=>['Comments']]);$articles->save($article);
Ao salvar associações hasMany, registros associados serão atualizados ou inseridos.
Para os caso em que o registro já tem registros associados no banco de dados, você
tem que escolher entre duas estrategias de salvamento:
append
Os registros associados são atualizados no banco de dados ou, se não econtrado nenhum
registro existente ele é inserido.
replace
Todos os registros existentes que não estão presentes nos registros fornecidos serão
removidos do banco dados. Apenas os registros fornecidos permanecerão (ou serão
inseridos).
Por padrão é utilizado a estratégia de salvamento append.
Consosule Associações HasMany para mais detalhes sobre como definir saveStrategy.
Sempre que você adiciona novos registros a uma associação existente, você sempre deve marcar
a propriedade de associação como “dirty”. Isso permite que o ORM saiba que a propriedade de
associação tem que ser persistida:
Ao salvar associações belongsToMany, o ORM espera um array de entidades nomeada com a
plural, underscored versão do nome da associação.
Por exemplo:
// Num controller.$data=['title'=>'First Post','tags'=>[['tag'=>'CakePHP'],['tag'=>'Framework']]];// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$article=$articles->newEntity($data,['associated'=>['Tags']]);$articles->save($article);
Ao converter dados de requisição em entidades, os métodos newEntity() e
newEntities() processarão ambos, arrays de propriedades, bem como uma lista de
ids na chave _ids. Utilizando a chave _ids facilita a criação de uma caixa
de seleção ou checkox para associações pertence a muitos (belongs to many). Consulte
a seção Convertendo Dados de Requisição em Entidades para mais informações.
Ao salvar associações belongsToMany, você tem que escolher entre duas estrategias
de salvamento:
append
Apenas novos links serão criados entre cada lado dessa associação. Essa estratégia
não destruirá links existentes, mesmo se não estiver presente no array de
entidades a serem salvas.
replace
Ao salvar, os links existentes serão removidos e novos links serão criados na tabela
de ligação. Se houver link existente no banco de dados para algumas das entidades
a serem salvas, esses links serão atualizados, e não excluídos para então serem salvos
novamente.
Por padrão é utilizado a estratégia replace. Sempre que você adiciona novos registros
a uma associação existente, você sempre deve marcar a propriedade de associação como “dirty”.
Isso permite que o ORM saiba que a propriedade de associação tem que ser persistida:
Sem a chamada ao método dirty() as tags atualizadas não serão salvas.
Frequentemente você se encontrará querendo fazer uma associação entre duas entidades
existentes, por exemplo. Um usuário que é autor de um artigo. Isso é feito usando o
método link(), como isso:
Ao salvar associações belongsToMany, pode ser relevente de salvar algumas
informações adicionais na tabela de ligação. No exemplo anterior de tags, poderia ser
o vote_type da pessoa que votou nesse artigo. O vote_type pode ser upvote
ou downvote e ele é representado por uma string. A relação é entre Users e Articles.
Salvando essa associaçao, e o vote_type é feito primeiramente adicionando alguns dados
em _joinData e então salvando a associação com link(), exemplo:
Em algumas situações a tabela ligando sua associação BelongsToMany, terá colunas
adicionais nela. CakePHP torna simples salvar propriendade nessas colunas.
Cada entidade em uma associação belongsToMany tem uma propriedade _joinData
que contém as colunas adicionais na tabela de ligação. Esses dados podem ser
um array ou uma instância de Entity. Por exemplo se Students BelongsToMany
Courses, nós poderíamos ter uma tabela de ligação que parece com:
id|student_id|course_id|days_attended|grade
Ao salvar dados, você pode popular as colunas adicionais na tabela de ligação
definindo dados na propriedade _joinData:
A propriedade _joinData pode ser uma entity, ou um array de dados, se você estiver
salvando entidades construídas a partir de dados de requisição. Ao salvar os dados de
tabela de ligação apartir de dados de requisição, seus dados POST devem parecer com:
$data=['first_name'=>'Sally','last_name'=>'Parker','courses'=>[['id'=>10,'_joinData'=>['grade'=>80.12,'days_attended'=>30]],// Other courses.]];$student=$this->Students->newEntity($data,['associated'=>['Courses._joinData']]);
As tabelas são capazes de armazenar dados representados em tipos básicos, como strings,
inteiros, flutuante, booleanos, etc. Mas também pode ser estendido para aceitar
tipos mais complexos, como arrays ou objects e serializar esses dados em tipos mais
simples que podem ser salvos em banco de dados.
Essa funcionalidade é alcançada usando o sistema de tipos personalizados (custom types system).
Consulte a seção Adicionando Tipos Personalizados para descobrir como criar tipo de coluna
personalizada (custom column Types):
// No config/bootstrap.phpuseCake\Database\TypeFactory;TypeFactory::map('json','Cake\Database\Type\JsonType');// No src/Model/Table/UsersTable.phpuseCake\Database\Schema\TableSchemaInterface;classUsersTableextendsTable{publicfunctiongetSchema():TableSchemaInterface{$schema=parent::getSchema();$schema->columnType('preferences','json');return$schema;}}
O código acima mapeia a coluna preferences para o tipo personalizado (custom type)
json. Isso significa que, ao obter dados dessa coluna, ele será desserializado
de uma string JSON no banco de dados e colocado em uma entidade como um array.
Da mesma forma, quando salvo, o array será transformado novamente em sua
representação de JSON:
Ao usar tipos complexos, é importante validar que os dados que você está
recebendo do usuário final são do tipo correto. A falha ao gerir corretamente
dados complexos, pode resultar em usuário mal-intencionados serem capazes de
armazenar dados que eles normalmente não seriam capaz.
Usar isso pode ser útil quando você estiver realizando operações
complexas no banco de dado sem monitoramento humano, por exemplo, dentro
de uma tarefa de Shell.
Note
Se você usar esse método em um controller, certifique-se de tratar a
PersistenceFailedException que pode ser lançada.
Se você quiser rastrear a entidade que falhou ao salvar, você pode usar o método
Cake\ORMException\PersistenceFailedException::getEntity():
Usando esse método você pode salvar várias entidades atomicamente. $entities
podem ser um array de entidades criadas usando newEntities() / patchEntities().
$options pode ter as mesmas opções aceitas por save():
$data=[['title'=>'First post','published'=>1],['title'=>'Second post','published'=>1],];// Prior to 3.6 use TableRegistry::get('Articles')$articles=TableRegistry::getTableLocator()->get('Articles');$entities=$articles->newEntities($data);$result=$articles->saveMany($entities);
O resultado será as entidades atualizadas em caso de sucesso ou false
em caso de falha.
Pode haver momentos em que atualizar linhas individualmente não é eficiente ou
necessária. Nesses casos, é mais eficiente usar uma atualização em massa para
modificar várias linhas de uma vez só:
// Publique todos artigos não publicadosfunctionpublishAllUnpublished(){$this->updateAll(['published'=>true],// fields['published'=>false]);// conditions}
Se você precisa de atualização em massa e usar expressões SQL, você precisará
usar um objeto de expressão como updateAll() usa prepared statements
por baixo dos panos:
Uma atualização em massa será considera bem-sucedida se uma ou mais linhas
forem atualizadas.
Warning
updateAll não irá disparar os eventos beforeSave/afterSave. Se você
precisa deles, primeiro carregue uma coleção de registros e então atualize
eles.
updateAll() é apenas por conveniência. Você também pode usar essa interface
mais flexível:
// Publique todos artigos não publicados.functionpublishAllUnpublished(){$this->query()->update()->set(['published'=>true])->where(['published'=>false])->execute();}