I'm having a problem with CakePHP's saveAll() I was hoping someone could shed some light on.
I have a form which collects information to be saved for two models... Person and Inquiry. I believe the data are being sent correctly, but it's simply not saving the Inquiry data. It's returning a validation error, but there is no validation set up in the Inquiry model, and if I remove 'deep' => true from the People controller, it saves those fields fine.
Data It Posts
Array
(
[Person] => Array
(
[first_name] => Test
[middle_name] =>
[last_name] => User
[gender] => M
[date_of_birth] => Array
(
[month] => 02
[day] => 07
[year] => 1994
)
[address] => 1234 Main St
[address_apt] =>
[address_city] => Somewhere
[address_state] => OH
[address_zip] => 304982
[address_country] => US
[phone_cell] => (555) 555-5555
[permission_to_text] => 1
[phone_home] => (555) 555-5556
[email_address] => test#user.com
[preferred_contact] => text_cell
[Inquiry] => Array
(
[admit_type] => FR
[admit_term] => FA2014
[parent_first_name] => Mom
[parent_last_name] => User
[parent_email] => mom#user.com
[hs_name] => Columbus Downtown High School
[hs_ceeb_id] => 365210
[hs_homeschooled] => 0
[hs_grad_year] => Array
(
[year] => 2014
)
[coll_name] =>
[coll_ceeb_id] =>
[coll_major] =>
[coll_year] =>
[admit_major] => 1
[admit_minor] => 4
)
[social_facebook] =>
)
)
Value of $this->Person->validationErrors after Posting
Array
(
[Inquiry] => Array
(
[hs_homeschooled] => Array
(
)
[coll_name] => Array
(
)
[coll_ceeb_id] => Array
(
)
[coll_major] => Array
(
)
[coll_year] => Array
(
)
)
)
Model - Inquiry
<?php
class Inquiry extends AppModel {
public $belongsTo = array('Person');
}
Controller
<?php
class PeopleController extends AppController {
public $helpers = array('Html', 'Form', 'Country', 'State', 'Major', 'Minor', 'Term');
public function index() {
if ($this->request->is('post')) {
$this->Person->create();
if ($this->Person->saveAll($this->request->data, array('deep' => true))) {
print_r($this->request->data);
$this->Session->setFlash(__('Your post has been saved.'));
return $this->redirect(array('action' => 'index'));
}
print_r($errors = $this->Person->validationErrors);
$this->set('errors', $errors = $this->Person->validationErrors);
$this->Session->setFlash(__('Unable to add.'));
}
}
}
Model::saveAll() is a wrapper of saveMany or saveAssociated.
Since you're not passing data in a numerical indexed array, i.e.
array(
0 => array(...),
1 => array(...),
)
it means saveAssociated is called. What you're apparently trying to do is to save a new Person together with the associations (Inquiry). If you read through the manual you'll come to this paragraph:
If neither of the associated model records exists in the system yet
(for example, you want to save a new User and their related Profile
records at the same time), you’ll need to first save the primary, or
parent model.
So you obviously need to save the Person first and then all associations.
Related
I have a form that produces the following array upon submition (see below).
I am using this data in my controller to perform several operations, after which I save each one individually. (Saving them all at once is not an option).
What I would need to do is to find a way to validate each of this models.
I have tried already:
$this->Model->set($pertinentData);
$this->Model2->set($pertinentData);
if($this->Model->validates() && $this->Model2->validates()){
//Do whatever
}
This produces unaccurate results, says it validates when I can see it doesn't and viceversa.
Anybody has any idea of a viable option? Ain't there a way to create a tableless model where I can define validation rules for these fields like:
Order.package_id
User.first_name
etc...
Any idea is appreciated. Below the array that the form produces.
Thanks.
Array
(
[Order] => Array
(
[id] => 15
[package_id] => 1743
[tariff_id] => 5470
[tarjeta] => 332
[numero_tarjeta] => 121204045050
[vencimiento_tarjeta] => 10/20
[cod_tarjeta] => 170
[titular_tarjeta] => JESUS CRISTO
[tarjeta_nacimiento] => 22/04/1988
[tarjeta_pais] => Argentina
[tarjeta_provincia] => Buenos Aires
[tarjeta_ciudad] => Ciudad
[tarjeta_cp] => 1428
[tarjeta_calle] => Calle
[tarjeta_numero] => 1477
[tarjeta_piso] => 2
)
[User] => Array
(
[id] =>
[email] => bla8#gmail.com
[phone] => 1568134449
[first_name] => Jesus
[last_name] => Something
[documento_tipo] => dni
[dni] => 335556666
[nacionalidad] => argentino
[birthdate] => 22/04/2019
)
[OrdersCompanion] => Array
(
[1] => Array
(
[first_name] => Chango
[last_name] => Mas
[documento_tipo] => dni
[dni] => 445556666
[nacionalidad] => argentino
[birthdate] => 30/02/2010
)
[1] => Array
(
[first_name] => Chango
[last_name] => Mas
[documento_tipo] => dni
[dni] => 445556666
[nacionalidad] => argentino
[birthdate] => 30/02/2010
)
)
)
You can usea tableless model by defining $useTable= false in the model. Like this
public $useTable = false;
Define all your custom validation and, of course, your schema (since your model has no table you have to define manually the model schema). Then in your controller, you must first indicate that it has no model, and then declare the $model variable. This is to avoid the automatic model-controller binding of cakePHP, your controller would look like this
public $useModel = false;
$model = ClassRegistry::init('ContactOperation');
Now your model is related to your controller as you want, and you can easily make your custom validation, previously defined.
$model->set($this->request->data);
if($model->validates()) {
$this->Session->setFlash(_('Thank you!'));
// do email sending and possibly redirect
// elsewhere for now, scrub the form
// redirect to root '/'.
unset($this->request->data);
$this->redirect('/');
} else {
$this->Session->setFlash(_('Errors occurred.'));
// display the form with errors.
}
You can find more detail from here
Hi I'm a novice in cakephp
However, I'm trying to make my blog with cakephp 3.0 which is stable version. I got stuck not taken database show view.ctp
Controller => "ListjobsController.php"
<?php
namespace App\Controller;
class ListjobsController extends AppController{
public function jobdetail(){
$jobdetail = $this->Listjobs->find('all');
$this->set( compact('jobdetail'));
}
}
Model =>Table => "ListjobsTable.php"
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class ListjobsTable extends Table{
public function initialize(array $config){
$this->table('listjobs');
$this->displayField('title');
$this->primaryKey('id');
$this->belongsToMany('Users',[
'foreignKey' => 'user_id'
]);
}
}
Template => Listjobs => "jobdetail.ctp"
<p><?= pr($jobdetail);exit; ?></p>
it does not appear that the current database information:
Cake\ORM\Query Object
(
[sql] => SELECT Listjobs.id AS `Listjobs__id`, Listjobs.user_id AS `Listjobs__user_id`, Listjobs.type_id AS `Listjobs__type_id`, Listjobs.cate_id AS `Listjobs__cate_id`, Listjobs.title AS `Listjobs__title`, Listjobs.location AS `Listjobs__location`, Listjobs.description AS `Listjobs__description`, Listjobs.skillsrequired AS `Listjobs__skillsrequired`, Listjobs.companyname AS `Listjobs__companyname`, Listjobs.website AS `Listjobs__website`, Listjobs.email AS `Listjobs__email`, Listjobs.password AS `Listjobs__password`, Listjobs.created AS `Listjobs__created`, Listjobs.modified AS `Listjobs__modified` FROM listjobs Listjobs
[params] => Array
(
)
[defaultTypes] => Array
(
[Listjobs.id] => integer
[id] => integer
[Listjobs.user_id] => integer
[user_id] => integer
[Listjobs.type_id] => integer
[type_id] => integer
[Listjobs.cate_id] => integer
[cate_id] => integer
[Listjobs.title] => string
[title] => string
[Listjobs.location] => string
[location] => string
[Listjobs.description] => text
[description] => text
[Listjobs.skillsrequired] => text
[skillsrequired] => text
[Listjobs.companyname] => string
[companyname] => string
[Listjobs.website] => string
[website] => string
[Listjobs.email] => string
[email] => string
[Listjobs.password] => string
[password] => string
[Listjobs.created] => datetime
[created] => datetime
[Listjobs.modified] => datetime
[modified] => datetime
)
[decorators] => 0
[executed] =>
[hydrate] => 1
[buffered] => 1
[formatters] => 0
[mapReducers] => 0
[contain] => Array
(
)
[matching] => Array
(
)
[extraOptions] => Array
(
)
[repository] => App\Model\Table\ListjobsTable Object
(
[registryAlias] => Listjobs
[table] => listjobs
[alias] => Listjobs
[entityClass] => \Cake\ORM\Entity
[associations] => Array
(
[0] => users
)
[behaviors] => Array
(
)
[defaultConnection] => default
[connectionName] => default
)
)
I can not understand why the data in the database does not appear.
hope you can help me!!!
The find() method always returns a Query object, that is the reason you cannot see the results but only the SQL that is going to be executed. If you are just starting with cake 3 there are a couple nice way in which you can see what the results of a query are:
debug($this->Table->find('all')->toArray());
That will give you an array of entities that were fetched from the database, the output may still be a bit confusing for you if you are not use to the idea of entities. SO here is another trick:
// In bootstrap.php
function dj($data)
{
debug(json_encode($data, JSON_PRETTY_PRINT));
}
And then:
dj($this->Table->find('all'));
do change in controller
namespace App\Controller;
class ListjobsController extends AppController{
public function jobdetail(){
$jobdetail = $this->Listjobs->find('all');
$this->set('listjobs', $jobdetail);
$this->set('_serialize', ['listjobs']);
}
}
now you can display data at view
Write in the appController.php file:
public function initialize()
{
$this->loadModel('Listjobs');
}
This should solve your problem.
I have a hasMany through with Course, User, and CourseMembership
I am trying to create an unsubscribe button on the Courses view. At the moment this is the array I have:
(
[Course] => Array
(
[id] => 1
[name] => Flying
[description] => Oh! The Magic!
[created] => 2014-03-05 14:45:21
[modified] => 2014-03-05 14:45:21
)
[Lesson] => Array
(
[0] => Array
(
[id] => 1
[title] => Growing Wings
[description] => Here's how you grow wings!
[course_id] => 1
[created] => 2014-03-05 14:19:38
[modified] => 2014-03-05 14:19:38
)
[1] => Array
(
[id] => 2
[title] => Taking Flight
[description] => You are finally ready to take flight.
[course_id] => 1
[created] => 2014-03-06 11:49:51
[modified] => 2014-03-06 11:49:51
)
)
[CourseMembership] => Array
(
[0] => Array
(
[id] => 1
[user_id] => 4
[course_id] => 1
)
)
)
I set up a CourseMemberships controller with a delete action.
But I am having trouble setting up the model in Courses to distinguish the appropriate ID of the CourseMembership where the user_id is the logged in user (CakeSession::read("Auth.User.id");) and the course is the current course.
Edit:
Here's the Courses unsubscribe() controller:
public function unsubscribe($id = null) {
$this->Course->id = $id;
$userid = CakeSession::read("Auth.User.id");
if (!$this->Course->exists()) {
throw new NotFoundException(__('Invalid course'));
}
$this->request->onlyAllow('post', 'delete');
if ($this->Course->CourseMembership->delete(array('CourseMembership.user_id'=> $userid))) {
$this->Session->setFlash(__('The course has been deleted.'));
} else {
$this->Session->setFlash(__('The course could not be deleted. Please, try again.'));
}
return $this->redirect(array('action' => 'index'));
}
Edit:
Here's the link I'm creating to unsubscribe.
<?php echo $this->Form->postLink(__('Unsubscribe'), array('controller' => 'CourseMemberships', 'action' => 'unsubscribe', $course['Course']['id'], CakeSession::read("Auth.User.id")), null, __('Are you sure you want to delete # %s?', $course['Course']['id'])); ?>
If I understand correctly, you want to delete the subscription to a specific course of the user logged in. So in your unsubscribe action in the Courses controller you may be have something like this:
// Courses Controller
public function unsubscribe($id = null){
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->Course->id = $id;
if (!$this->Course->exists()) {
throw new NotFoundException(__d('Course', 'Invalid course'));
}
if($this->Course->CourseMembership->unsubscribe($id, CakeSession::read('Auth.User.id'))){
$this->Session->setFlash(__d('Course', 'Unsubscribed!'));
$this->redirect(array('controller' => 'courses', 'action' => 'go_here_etc'));
}
$this->Session->setFlash(__d('Course', 'Attention! Not Unsubscribed!'));
$this->redirect(array('controller' => 'courses', 'action' => 'go_here_etc'));
}
//CourseMembership Model
public function unsubscribe($courseId, $userId){
// I assume that each user can subscribe only one time the same course. This calls the ids inputted in the unsubscribe link.
return $this->deleteAll(array('CourseMembership.course_id' => $courseId, 'CourseMembership.user_id' => $userId));
}
I'm wondering what the cleanest way is to implement a cakephp form where 1 control is a multi-select and the rest are text fields or single-selects, and then the data is inserted as multiple rows with a saveall(). So for example a form is selected with these values:
textfield A
value=Foo
mulit-select B
values=US,Mexico,Canada
single=select C
value=10
and so I want to insert these rows into the database with a saveall():
Foo,US,10
Foo,Mexico,10
Foo,Canada,10
Now I know in the add view I can use this format for the input statement:
input('Model.0.field1',...)
but I'm wondering if I can mix that in that same form with inputs formatted like
input('Model.field2',....).
Update:
When I mix and match the single-select and multiple-select controls, the form data gets submitted like this:
Array
(
[Alert] => Array
(
[schedule_id] => 75
[user_id] => 6
[0] => Array
(
[frequency] => Array
(
[0] => WEEKLY
[1] => MONTHLY
)
)
[limit_value] => .03
[limit_adjustment] => 0
[type] => LIMIT
[disabled] => 0
)
)
I tried passing that data into saveall() but it treats it like a single record.
Update2: I think saveAll() requires that the multiple rows of data be formatted like this:
Array
(
[Article] => Array(
[0] => Array
(
[title] => title 1
)
[1] => Array
(
[title] => title 2
)
)
)
So it looks like after the submit I'm going to need some javascript code that will restructure the array.
I have something that works... I'm not sure if it takes full advantage of all of cake's "automagic" capabilities, but I don't think it's too convoluted.
So I just added the following code to my controller's add function:
if (!empty($this->data)) {
//debug($this->data, true);
/* begin custom code */
$multiselect = $this->data['Alert']['entity_id'];
$tmp2 = array();
foreach ($multiselect as $item)
{
$tmp = $this->data['Alert'];
$tmp['entity_id'] = $item;
array_push($tmp2,$tmp);
}
$this->data['Alert'] = $tmp2;
debug($this->data,true);
/* end custom code */
$this->Alert->create();
//restructure data
if ($this->Alert->saveAll($this->data['Alert'])) {
$this->Session->setFlash(__('The alert has been saved', true));
//$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The alert could not be saved. Please, try again.', true));
}
and that converts my data to this:
Array
(
[Alert] => Array
(
[0] => Array
(
[schedule_id] => 74
[entity_id] => 1
[user_id] => 6
[frequency] => HOURLY
[limit_value] => .02
[limit_adjustment] => 0
[type] => LIMIT
[disabled] => 1
)
[1] => Array
(
[schedule_id] => 74
[entity_id] => 2
[user_id] => 6
[frequency] => HOURLY
[limit_value] => .02
[limit_adjustment] => 0
[type] => LIMIT
[disabled] => 1
)
)
)
Like the example in cakephp manual, http://book.cakephp.org/view/1323/Containable#Containing-deeper-associations-1325, i need to fetch data from a model through a condition on its association model.
I have:
Model Language:
class Language extends AppModel {
var $name = 'Language';
var $actsAs = array('Containable');
var $hasMany = array(
'LanguageTranslation' => array(
'className' => 'LanguageTranslation',
'foreignKey' => 'language_id'
)
);
}
And the association, ModelTranslation
class LanguageTranslation extends AppModel {
var $name = 'LanguageTranslation';
var $belongsTo = array(
'Language'
);
}
when i do:
$language_array = $this->controller->Language->find('all', array(
'contain' => "LanguageTranslation.id = 1"
));
i receive all the languages, not only one (because id in LanguageTranslation is unique). The result need to be one!
SO, with
debug($language_array);
result is:
Array
(
[0] => Array
(
[Language] => Array
(
[id] => 1
[code] => it
[locale] => ita
)
[LanguageTranslation] => Array
(
[0] => Array
(
[id] => 1
[language_id] => 1
[language] => italiano
)
)
)
[1] => Array
(
[Language] => Array
(
[id] => 2
[code] => en
[locale] => eng
)
[LanguageTranslation] => Array
(
)
)
[2] => Array
(
[Language] => Array
(
[id] => 3
[code] => de
[locale] => ger
)
[LanguageTranslation] => Array
(
)
)
)
Why i don't catch only the Language with id = 1?
linkable behavior will do the trick.
You can download it on:
https://github.com/rafaelbandeira3/linkable
MODEL
var $actsAs = array('Linkable');
CONTROLLER
$language_array = $this->Language->find('all', array(
'link' => array('LanguageTranslation'),
'conditions' => array("LanguageTranslation.id = 1")
));
The conditions in containable apply only to the models inside the containment. Because the main query doesn't have any conditions it fetches every row in the table. In general you don't use containable to restrict the main query but to limit the way the query goes through the containment tree (very handy when you need to go through 3-4 levels of recursiveness where the results can get quite bloated if not contained).
In this case in particular if you are trying to get the data of a certain Language/LanguageTranslation pair, you can just pull the data from the LanguageTranslation model.
$this->Language->LanguageTranslation->find(
'first',
array(
'conditions' => array( 'LanguageTranslation.id' => 1 ),
'recursive' => 1
)
);
In regards to Juhana's answer: (which initially calls find on the Language Model), You can supply conditions for the parent model which target child models.
Contain in a separate call.
<?php
$this->Language->contain(array(
'LanguageTranslation'
));
$lang = $this->Language->find('first', array(
'conditions' => array('LanguageTranslation.id' => 1)
));
?>
Contain in one call.
<?php
$lang = $this->Language->find('first', array(
'conditions' => array('LanguageTranslation.id' => 1),
'contain' => array('LanguageTranslation')
));
?>
If you need to apply conditions to the child models just add the conditions array to the child model's index in the contain call. For example:
<?php
$this->Language->contain(array(
'LanguageTranslation' => array(
'conditions' => array('<LanguageTranslationConditionHere>')
)
));
?>
Hope this helps!
-Andrew