BelongsToMany retrieving and deleting correctly, not updating/saving in CakePHP 3.x - cakephp

CakePHP 3.8.13 - I have a table model which CAN retrieve the related data from a table and remove it, but when updating/adding an entry, it does not change this data for some reason. So it seems like a correct setup, yet it does not update/add them. In short, it's a "Questions" table and a "Divisions" table, with a relation table called "questions_divisions". In this relation table there is a question_id and a division_id column. If I manually add or remove entries there, it all seems OK. Just when doing patchEntity and save, it does not do it. This is the post data:
'name' => string 'Some name' (length=8)
'questiontype_id' => string '1' (length=1)
'extra' => string '' (length=0)
'answer' => string '' (length=0)
'datafield' => string 'Person.firstname' (length=16)
'divisions' =>
array (size=1)
'_ids' =>
array (size=3)
0 => string '1' (length=1)
1 => string '7' (length=1)
2 => string '5' (length=1)
This is the code that is executed (so the above post data, is $this->request->getData()):
$question = $this->Questions->patchEntity($question, $this->request->getData());
if ($this->Questions->save($question)) {
All this is more or less the same that's happening elsewhere in the application for a different model, which actually DOES add/update the divisions. I also cleared the /tmp/cache just in case, didn't seem to do the trick. I'm at a loss here.
This is the whole table model:
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
/**
* Questions Model
*
* #property \App\Model\Table\QuestiontypesTable|\Cake\ORM\Association\BelongsTo $Questiontypes
* #property \App\Model\Table\ActivitiesTable|\Cake\ORM\Association\BelongsToMany $Activities
* #property \App\Model\Table\BookingformsTable|\Cake\ORM\Association\BelongsToMany $Bookingforms
* #property \App\Model\Table\DivisionsTable|\Cake\ORM\Association\BelongsToMany $Divisions
*
* #method \App\Model\Entity\Question get($primaryKey, $options = [])
* #method \App\Model\Entity\Question newEntity($data = null, array $options = [])
* #method \App\Model\Entity\Question[] newEntities(array $data, array $options = [])
* #method \App\Model\Entity\Question|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
* #method \App\Model\Entity\Question|bool saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
* #method \App\Model\Entity\Question patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* #method \App\Model\Entity\Question[] patchEntities($entities, array $data, array $options = [])
* #method \App\Model\Entity\Question findOrCreate($search, callable $callback = null, $options = [])
*/
class QuestionsTable extends Table {
public function beforeFind ($event, $query, $options, $primary) {
$order = $query->clause('order');
if ($order === null || !count($order)) {
$query->order([$this->getAlias() . '.name' => 'ASC']);
}
}
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config) {
parent::initialize($config);
$this->setTable('questions');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->addBehavior('Search.Search');
$this->addBehavior('Page');
$this->hasMany('Questions_divisions', [
'foreignKey' => 'question_id'
]);
$this->belongsTo('Questiontypes', [
'foreignKey' => 'questiontype_id',
'joinType' => 'INNER'
]);
$this->belongsToMany('Activities', [
'foreignKey' => 'question_id',
'targetForeignKey' => 'activity_id',
'joinTable' => 'activities_questions'
]);
$this->belongsToMany('Bookingforms', [
'foreignKey' => 'questions_id',
'targetForeignKey' => 'bookingforms_id',
'joinTable' => 'questions_bookingforms'
]);
$this->belongsToMany('Divisions', [
'foreignKey' => 'question_id',
'targetForeignKey' => 'division_id',
'joinTable' => 'questions_divisions'
]);
//Setup searchfield
$this->behaviors()->Page->searchSetup($this->behaviors()->Search->searchManager(), $this->searchForm());
}
/**
* Default validation rules.
*
* #param \Cake\Validation\Validator $validator Validator instance.
* #return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator) {
$validator
->integer('id')
->allowEmpty('id', 'create');
$validator
->scalar('name')
->maxLength('name', 500)
->requirePresence('name', 'create')
->notEmpty('name');
$validator
->scalar('extra')
->maxLength('extra', 500)
->allowEmpty('extra');
$validator
->scalar('answer')
->maxLength('answer', 1500)
->allowEmpty('answer');
$validator
->scalar('datafield')
->maxLength('datafield', 500)
->requirePresence('datafield', 'create')
->notEmpty('datafield');
return $validator;
}
/**
* Returns a rules checker object that will be used for validating
* application integrity.
*
* #param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* #return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules) {
$rules->add($rules->existsIn(['questiontype_id'], 'Questiontypes'));
return $rules;
}
/**
* Returns an array which describes the search form
*
* #return Array with search setup
*/
public function searchForm() {
//Set up search form
$search_data = [['id' => 'name', 'label' => 'Naam', 'type' => 'textfield', 'fields' => ['name']],
['id' => 'Questiontypes_id', 'label' => 'Vraagtype', 'type' => 'multiselect', 'fields' => ['Questiontypes.id'], 'options' => $this->Questiontypes->find('list')],
['id' => 'datafield', 'label' => 'Dataveld', 'type' => 'textfield', 'fields' => ['datafield']]
];
return $search_data;
}
}

Seems like adding this column in the $_accessible array (inside the Model/Entity) does the trick... That's why it could retrieve the data, but not edit it - hence "accessible" maybe? Not sure if that's the correct name for it but hey..
protected $_accessible = [
'name' => true,
'extra' => true,
'answer' => true,
'datafield' => true,
'questiontype_id' => true,
'questiontype' => true,
'activities' => true,
'bookingforms' => true,
'divisions' => true // this did the trick!
];

Related

How to Build Multi-step Forms with Preview data fill page in drupal 8?

many days I working on drupal 8 custom form module which is the multi-step form I have complete this using SessionManagerInterface that's work fine but I want to display all data in one page before submit how can I achieve this .here I include snapshot here
demo.routing.yml
demo.multistep_one:
path: '/demo/multistep-one'
defaults:
_form: '\Drupal\demo\Form\Multistep\MultistepOneForm'
_title: 'First form'
requirements:
_permission: 'access content'
demo.multistep_two:
path: '/demo/multistep-two'
defaults:
_form: '\Drupal\demo\Form\Multistep\MultistepTwoForm'
_title: 'Second form'
requirements:
_permission: 'access content'ent'
MultistepFormBase.php
namespace Drupal\demo\Form\Multistep;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\SessionManagerInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class MultistepFormBase extends FormBase {
/**
* #var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* #var \Drupal\Core\Session\SessionManagerInterface
*/
private $sessionManager;
/**
* #var \Drupal\Core\Session\AccountInterface
*/
private $currentUser;
/**
* #var \Drupal\user\PrivateTempStore
*/
protected $store;
/**
* Constructs a \Drupal\demo\Form\Multistep\MultistepFormBase.
*
* #param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* #param \Drupal\Core\Session\SessionManagerInterface $session_manager
* #param \Drupal\Core\Session\AccountInterface $current_user
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, SessionManagerInterface $session_manager, AccountInterface $current_user) {
$this->tempStoreFactory = $temp_store_factory;
$this->sessionManager = $session_manager;
$this->currentUser = $current_user;
$this->store = $this->tempStoreFactory->get('multistep_data');
}
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('user.private_tempstore'),
$container->get('session_manager'),
$container->get('current_user')
);
}
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Start a manual session for anonymous users.
if ($this->currentUser->isAnonymous() && !isset($_SESSION['multistep_form_holds_session'])) {
$_SESSION['multistep_form_holds_session'] = true;
$this->sessionManager->start();
}
$form = array();
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Submit'),
'#button_type' => 'primary',
'#attributes' => array(
'class' => array(
'btn btn-register'
),
),
);
return $form;
}
MultistepOneForm.php which child form
namespace Drupal\demo\Form\Multistep;
use Drupal\Core\Form\FormStateInterface;
class MultistepOneForm extends MultistepFormBase {
/**
* {#inheritdoc}.
*/
public function getFormId() {
return 'multistep_form_one';
}
/**
* {#inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$form['fname'] = array(
'#type' => 'textfield',
'#title' => $this->t('Your name'),
'#default_value' => $this->store->get('fname') ? $this->store->get('fname') : '',
'#attributes' => array(
'class' => array(
'form-control'
),
),
);
$form['lname'] = array(
'#type' => 'textfield',
'#title' => $this->t('Your Last Name'),
'#default_value' => $this->store->get('lname') ? $this->store->get('lname') : '',
'#attributes' => array(
'class' => array(
'form-control'
),
),
$form['actions']['submit']['#value'] = $this->t('Continue');
return $form;
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('fname', $form_state->getValue('fname'));
$this->store->set('lname', $form_state->getValue('lname'));
$form_state->setRedirect('demo.multistep_two');
}
}
MultistepTwoForm.php
namespace Drupal\demo\Form\Multistep;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
class MultistepTwoForm extends MultistepFormBase {
/**
* {#inheritdoc}.
*/
public function getFormId() {
return 'multistep_form_two';
}
/**
* {#inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$form['jfname'] = array(
'#type' => 'textfield',
'#title' => $this->t('Joint Account Holder First Name'),
'#attributes' => array(
'class' => array(
'form-control'
),
),
);
$form['jlname'] = array(
'#type' => 'textfield',
'#title' => $this->t('Joint Account Holder Last Name'),
'#attributes' => array(
'class' => array(
'form-control'
),
),
);
$form['actions']['previous'] = array(
'#type' => 'link',
'#title' => $this->t('Previous'),
'#url' => Url::fromRoute('demo.multistep_one'),
'#suffix' => '</div>',
'#attributes' => array(
'class' => array(
'btn btn-register'
),
),
);
$form['actions']['submit']['#value'] = $this->t('Continue');
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('jfname', $form_state->getValue('jfname'));
$this->store->set('jlname', $form_state->getValue('jlname'));
// $form_state->setRedirect('demo.multistep_three');
}
}
here my concern is raised How to display all data according to my snapshot.what action needs to be taken display data. one thing is that i have 3 month experience about drupal so can't take decision what i will do? this code is example of www.sitepoint.com and I take form three and session data is display in label whether it good or not i don't know give me direction appropriate
thanks in advance
In your setup, you are submitting the multiform in MultistepTwoForm.
Change this as follows:
Build one more form: MultiStepPreview. This will hold your preview.
Do Not submit the form in MultistepTwoForm but redirect the form to MultiStepPreview using $form_state->setRedirect()
In MultiStepPreview:
read the keys in the store
build a form element containing the preview (I made a html table to contain the preview)
submit the form.
class OverviewForm extends MultiStepFormBase {
public function buildForm(array $form, FormStateInterface $form_state) {
// get data from the store
$your_details = $this->store->get('your_details');
$order = $this->store->get('order_details');
// make a HTML table containing the data in store
$markup = '<table>';
// loop through $your_details and $your_order and put them in $markup
$form = parent::buildForm($form, $form_state);
$form['preview'] = [
'#markup' => $markup,
];
//in my setup all the buttons are build by MultiStepFormBase
return $form;
}
}

Cakephp 3 how to add beforeFind on table

I am reading the documentation and there seems to be no example of how to use the beforeFind in Cakephp 3. I thought maybe I could do it like I did it in 2, but that did not work. Here are the two ways I did it.
public function beforeFind(Event $event, Query $query, array $options, $primary){
$primary = (bool) $primary;
if(!empty($options['pure'])) {
$query->hydrate(false)
->join([
'table' => 'main_'.$this->store_id,
'alias' => 'c',
'type' => 'LEFT',
'conditions' => 'c.id = Stores.MAIN_ID',
]);
}
}
Second way:
public function initialize(array $config) {
if(!isset($config['store_id'])) throw new Exception('You must provide a store id');
$this->store_id = $config['store_id'];
$this->entityClass('P1\Model\Entity\Store');
$this->eventManager()->attach([$this,'addMain'],'beforeFind');
}
public function addMain(Event $event, Query $query, array $options, $primary){
$primary = (bool) $primary;
if(!empty($options['pure'])) {
$query->hydrate(false)
->join([
'table' => 'main_'.$this->store_id,
'alias' => 'c',
'type' => 'LEFT',
'conditions' => 'c.id = Stores.MAIN_ID',
]);
}
}
What am I doing wrong?
You must use method applyOptions from class Query.
$this->Posts->find()->applyOptions(['pure'=>true])->all()

how to use countercache for different alias in cakephp model?

I have the following models: Attachment and PurchaseOrder hence the datatables attachments and purchase_orders.
In PurchaseOrder, I have
class PurchaseOrder extends AppModel {
public $hasMany = array(
'Pdf' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array(
'Pdf.model' => 'PurchaseOrder'
)
),
'Zip' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array(
'Zip.model' => 'PurchaseOrder'
)
),
In Attachment, I have the following:
public $belongsTo = array(
'PurchaseOrder' => array(
'className' => 'PurchaseOrder',
'foreignKey' => 'foreign_key',
'counterCache' => array(
'attachment_count' => array('Pdf.model' => 'PurchaseOrder'),
)
),
My problem is when I try to use $this->PurchaseOrder->Zip->save($data); I run into problem because the alias Pdf is not found.
How do I overcome this while maintaining the countercache behavior of updating the attachment_count inside purchase_orders?
Note that if a PurchaseOrder is associated with 3 Pdf Attachments and 2 Zip Attachments, the attachment_count should read 3.
I am using cakephp 2.4.2
I stopped using counterCache and used afterSave instead.
/**
* Called after each successful save operation.
*
* #param boolean $created True if this save created a new record
* #param array $options Options passed from Model::save().
* #return void
* #link http://book.cakephp.org/2.0/en/models/callback-methods.html#aftersave
* #see Model::save()
*/
public function afterSave($created, $options = array()) {
// update the PurchaseOrder based on pdf
if (isset($this->data['Pdf'])) {
$this->updatePurchaseOrderAttachmentCount($this->data);
}
}
/**
*
* #param Array $data where we expect key Pdf
*/
public function updatePurchaseOrderAttachmentCount($data) {
// check for Pdf
$pdfSet = isset($data['Pdf']);
if (!$pdfSet) {
throw new Exception('we expect Pdf as a key in your $data');
}
// check for foreign_key and model
$pdfFKSet = isset($data['Pdf']['foreign_key']);
$pdfModelSet = isset($data['Pdf']['model']);
if (!$pdfFKSet || !$pdfModelSet) {
throw new Exception('we expect foreign_key and model as keys in your $data["Pdf"]');
}
$count = $this->find('count', array(
'conditions' => array(
'model' => 'PurchaseOrder',
'type' => 'application/pdf',
'foreign_key' => $data['Pdf']['foreign_key'],
)
));
if (!is_numeric($count)) {
throw new Exception('we expect numeric in the $count');
}
$poData = array(
'PurchaseOrder' => array(
'id' => $data['Pdf']['foreign_key'],
'attachment_count' => $count,
)
);
return $this->PurchaseOrder->save($poData);
}
I know this post is very old, but I recently faced a similar issue and found another way to handle the same problem. You can continue using the counterCache approach with multiple counterCache, the only difference would be, you need to add you association on the fly, in the __construct method of your model.
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
/**
* When using Aliases, associations need to be added on the fly, otherwise, the CounterScope conditions would result in an SQL Error, during counterCache updates
*/
$this->bindModel(
array('belongsTo' => array(
'PurchaseOrder' => array(
'className' => 'PurchaseOrder',
'foreignKey' => 'foreign_key',
'counterCache' => array(
'attachment_count' => array($this->alias . '.model' => 'PurchaseOrder'),
'attachment_count' => array($this->alias . '.model' => 'PurchaseOrder')
)
)
)
)
);
}

Stuck with HasMany through saving and error array_merge()

I'm trying to set up a pretty simple website to create and edit recipes ('Recettes' in french).
I'm an experienced frontend developer with a good knowledge (not advanced) of php, and I have been working on CakePhp with a team of developers in the past. I'm kind of learning the bases of starting a project from scratch and it is really not that easy.
I've set up a project, database (Ingredients, Recette and IngredientRecette) and used Bake to generate most of the code.
I have a working CRUD workflow for my 3 models and the only thing left to do is to be able to add ingredients from the Recette/add page but i'm stuck. I followed the instructions on the cake php website (http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model) and other website (cause the help on cakephp's doc is really not that helpful) and I beleive I have setup a correct Hasmany Through relationship.
But the saveAll function doesn't save anything in the join model (IngredientRecette) and I've came accross a particular error (see below) after I've successfully solved another one that took me a couple of hours to work out !
So I have the feeling that I've checked, and double checked, and triple checked everything in my code and I've read every questions and answers on forums etc...
And I am stuck with this problem, maybe there is something obvious I didn't figured, or maybe and definitely don't understand how Cakephp really wordk :(
Anyway, thanks in advance for those who will be able to bring there advices or help on this :
Here is the error I get after I submitted /recettes/add (I added some form elements to add an ingredient and a quantity to my Recette) :
Warning (2): array_merge() [function.array-merge]: Argument #2 is not an array [CORE\Cake\Model\Model.php, line 2250]
Here is the array I'm passing to the saveAll() method in the controller (output from debug()):
array(
'Recette' => array(
'auteur' => '7',
'nom' => 'Nouvelle Recette',
'cout' => 'Pas cher',
'niveau' => '5',
'tps_realisation' => '30 min',
'nb_personnes' => '6',
'description' => 'Description data',
'remarque' => ''
),
'AssociatedIngredient' => array(
'id_ingredient' => '6',
'quantite' => '70 cl',
'id_recette' => '7654'
)
)
Here is my controller code :
<?php
App::uses('AppController', 'Controller');
/**
* Recettes Controller
*
* #property Recette $Recette
*/
class RecettesController extends AppController {
/**
* index method
*
* #return void
*/
public function index() {
$this->Recette->recursive = 0;
$this->set('recettes', $this->paginate());
}
/**
* view method
*
* #param string $id
* #return void
*/
public function view($id = null) {
$this->Recette->id = $id;
if (!$this->Recette->exists()) {
throw new NotFoundException(__('Invalid recette'));
}
$this->set('recette', $this->Recette->read(null, $id));
}
/**
* add method
*
* #return void
*/
public function add() {
if ($this->request->is('post')) {
debug($this->request->data);
$this->Recette->create();
if ($this->Recette->saveAll($this->request->data)) {
$this->Session->setFlash(__('The recette has been saved'));
//$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The recette could not be saved. Please, try again.'));
}
}
$this->loadModel('Ingredient');
$liste_ingr = $this->Ingredient->find('all');
$this->set('liste_ingr', $liste_ingr);
}
The IngredientRecette model
<?php
App::uses('AppModel', 'Model');
/**
* Recette Model
*
* #property IngredientRecette $ingredient
*/
class Recette extends AppModel {
/**
* Use database config
*
* #var string
*/
public $useDbConfig = 'default';
/**
* Use table
*
* #var mixed False or table name
*/
public $useTable = 'recette';
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_recette';
/**
* Display field
*
* #var string
*/
public $displayField = 'nom';
public $recursive = 2;
/**
* Validation rules
*
* #var array
*/
public $validate = array(
'id_recette' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'auteur' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'nom' => array(
'notempty' => array(
'rule' => array('notempty'),
),
),
'niveau' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'nb_personnes' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* hasMany associations
*
* #var array
*/
public $hasMany = array(
'AssociatedIngredient' => array(
'className' => 'IngredientRecette',
'foreignKey' => 'id_recette',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
And IngredientRecette model (the join model)
<?php
App::uses('AppModel', 'Model');
/**
* IngredientRecette Model
*
* #property ingredient $ingredient
* #property recette $recette
*/
class IngredientRecette extends AppModel {
/**
* Use database config
*
* #var string
*/
public $useDbConfig = 'default';
/**
* Use table
*
* #var mixed False or table name
*/
public $useTable = 'ingredient_recette';
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_ingredient_recette';
public $recursive = 2;
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'IngredientLiaison' => array(
'className' => 'Ingredient',
'foreignKey' => 'id_ingredient',
'conditions' => '',
'fields' => '',
'order' => ''
),
'RecetteLiaison' => array(
'className' => 'Recette',
'foreignKey' => 'id_recette',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
So nothing is saved in the join model and I have this warning...
Any help appreciated and of course please let me know if anything is unclear !
thanks a lot !!
I think your data is not formatted correctly. How about trying this instead (notice the extra array I put inside Associated Ingredient). Since recipe hasMany associated ingredient, the data for Associated Ingredient should be a series of arrays.
array(
'Recette' => array(
'auteur' => '7',
'nom' => 'Nouvelle Recette',
'cout' => 'Pas cher',
'niveau' => '5',
'tps_realisation' => '30 min',
'nb_personnes' => '6',
'description' => 'Description data',
'remarque' => ''
),
'AssociatedIngredient' => array(
array(
'id_ingredient' => '6',
'quantite' => '70 cl',
'id_recette' => '7654'
),
array( /*ingredient 2 data*/ ),
)
);

Why am I having trouble saving objects to the database in a typo3 extension?

Why won't my user and coupon objects get saved to the database when a new record is created?
I am using typo3 v 4.5.30 and making a little extension (my first) to manage some coupons. When I create a coupon I save the creator (a frontenduser) and it gets saved to the DB correctly. But when I do the same thing for a coupon user that user ( and the coupon ) do not get saved to the db. Consider this code frag which attempts to save the user and the coupon in a usedcoupon table. The usedcoupon table basically just has 2 columns one for the user and one for the coupon.
To get a usedcoupon object I called the objectmanagers create method. The user and coupon objects I already have and look right when I var_dump them. Even when I get them from the usedcoupon object they look ok but they don't get saved to the db even when the new record gets created. This code in in my CouponController in an action method.
$used = $this->objectManager>create('Tx_BpsCoupons_Domain_Model_UsedCoupon');
$used->setCoupon($coupon);
$used->setUser($user);
$used->setGuest("myemail#ddd.com");
$userx = $used->getUser();
$coupx = $used->getCoupon();
/// var_dumps of userx and coupx show good objects
$this->usedCouponRepository->add($used);
//after this I can examine the db and see the new record but the user and coupon fields are empty, and no errors are seen
Thanks
PS here is my TCA from Usedcoupon.php
<?php
if (!defined ('TYPO3_MODE')) {
die ('Access denied.');
}
$TCA['tx_bpscoupon_domain_model_usedcoupon'] = array(
'ctrl' => $TCA['tx_bpscoupon_domain_model_usedcoupon']['ctrl'],
'interface' => array(
'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, user, coupon, guest',
),
'types' => array(
'1' => array('showitem' => 'sys_language_uid;;;;1-1-1, l10n_parent, l10n_diffsource, hidden;;1, user, coupon, guest,--div--;LLL:EXT:cms/locallang_ttc.xml:tabs.access,starttime, endtime'),
),
'palettes' => array(
'1' => array('showitem' => ''),
),
'columns' => array(
'sys_language_uid' => array(
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.language',
'config' => array(
'type' => 'select',
'foreign_table' => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xml:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xml:LGL.default_value', 0)
),
),
),
'l10n_parent' => array(
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
'config' => array(
'type' => 'select',
'items' => array(
array('', 0),
),
'foreign_table' => 'tx_bpscoupon_domain_model_usedcoupon',
'foreign_table_where' => 'AND tx_bpscoupon_domain_model_usedcoupon.pid=###CURRENT_PID### AND tx_bpscoupon_domain_model_usedcoupon.sys_language_uid IN (-1,0)',
),
),
'l10n_diffsource' => array(
'config' => array(
'type' => 'passthrough',
),
),
't3ver_label' => array(
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.versionLabel',
'config' => array(
'type' => 'input',
'size' => 30,
'max' => 255,
)
),
'hidden' => array(
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.hidden',
'config' => array(
'type' => 'check',
),
),
'starttime' => array(
'exclude' => 1,
'l10n_mode' => 'mergeIfNotBlank',
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.starttime',
'config' => array(
'type' => 'input',
'size' => 13,
'max' => 20,
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => array(
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
),
),
),
'endtime' => array(
'exclude' => 1,
'l10n_mode' => 'mergeIfNotBlank',
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.endtime',
'config' => array(
'type' => 'input',
'size' => 13,
'max' => 20,
'eval' => 'datetime',
'checkbox' => 0,
'default' => 0,
'range' => array(
'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y'))
),
),
),
'user' => array(
'exclude' => 0,
'label' => 'LLL:EXT:bpscoupon/Resources/Private/Language/locallang_db.xml:tx_bpscoupon_domain_model_usedcoupon.user',
'config' => array(
'type' => 'select',
'foreign_table' => 'fe_users'
),
),
'coupon' => array(
'exclude' => 0,
'label' => 'LLL:EXT:bpscoupon/Resources/Private/Language/locallang_db.xml:tx_bpscoupon_domain_model_usedcoupon.coupon',
'config' => array(
'type' => 'select',
'foreign_table' => 'tx_bpscoupons_domain_model_coupon'
),
),
'guest' => array(
'exclude' => 0,
'label' => 'LLL:EXT:bpscoupon/Resources/Private/Language/locallang_db.xml:tx_bpscoupon_domain_model_usedcoupon.guest',
'config' => array(
'type' => 'input',
'size' => 30,
'eval' => 'trim'
),
),
),
);
?>
PPS adding usedcoupon model code:
<?php
/***************************************************************
* Copyright notice
*
* (c) 2013 Cory Taylor <cory#bigplayersystems.com>, Big Player Systems
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
*
*
* #package bps_coupons
* #license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3 or later
*
*/
class Tx_BpsCoupons_Domain_Model_UsedCoupon extends Tx_Extbase_DomainObject_AbstractEntity {
/**
* Used By
*
* #var Tx_BpsCoupons_Domain_Model_FrontendUser
*/
protected $user;
/**
* Returns the $user
*
* #return Tx_BpsCoupons_Domain_Model_FrontendUser $user
*/
public function getUser() {
echo "<br/>" . __FUNCTION__ . __LINE__ . " <br/>";
return $this->user;
}
/**
* Sets the $user
*
* #param #param Tx_BpsCoupons_Domain_Model_FrontendUser $user
* #return void
*/
public function setUser(Tx_BpsCoupons_Domain_Model_FrontendUser $user) {
$this->user = $user;
}
/**
* the used coupon
*
* #var Tx_BpsCoupons_Domain_Model_Coupon
*/
protected $coupon;
/**
* Returns the $coupon
*
* #return Tx_BpsCoupons_Domain_Model_Coupon $coupon
*/
public function getCoupon() {
return $this->coupon;
}
/**
* Sets the $coupon
*
* #param #param Tx_BpsCoupons_Domain_Model_Coupon $coupon
* #return void
*/
public function setCoupon(Tx_BpsCoupons_Domain_Model_Coupon $coupon) {
$this->coupon = $coupon;
}
/**
* the guest email
*
* #var string
*/
protected $guest;
/**
* Returns the $guest
*
* #return string $guest
*/
public function getGuest() {
return $this->guest;
}
/**
* Sets the $guest email
*
* #param string $guest
* #return void
*/
public function setGuest( $guest) {
$this->guest = $guest;
}
}
?>
PPPS: I tried adding a basic text field for email addresses but it turns out they don't get saved either. I had thought the issue was that the user and coupon filed were references to rows in other tables but now it turns out that simpler things do not get saved either.
PP PP S: may as well look into my ext_tables file too:
<?php
if (!defined('TYPO3_MODE')) {
die ('Access denied.');
}
Tx_Extbase_Utility_Extension::registerPlugin(
$_EXTKEY,
'Bpscoupons',
'BPS_Coupons'
);
t3lib_extMgm::addStaticFile($_EXTKEY, 'Configuration/TypoScript', 'BPS_Coupons');
t3lib_extMgm::addLLrefForTCAdescr('tx_bpscoupons_domain_model_coupon', 'EXT:bps_coupons/Resources/Private/Language/locallang_csh_tx_bpscoupons_domain_model_coupon.xml');
t3lib_extMgm::allowTableOnStandardPages('tx_bpscoupons_domain_model_coupon');
$TCA['tx_bpscoupons_domain_model_coupon'] = array(
'ctrl' => array(
'title' => 'LLL:EXT:bps_coupons/Resources/Private/Language/locallang_db.xml:tx_bpscoupons_domain_model_coupon',
'label' => 'name',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'dividers2tabs' => TRUE,
'sortby' => 'sorting',
'versioningWS' => 2,
'versioning_followPages' => TRUE,
'origUid' => 't3_origuid',
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l10n_parent',
'transOrigDiffSourceField' => 'l10n_diffsource',
'delete' => 'deleted',
'enablecolumns' => array(
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
),
'searchFields' => 'name,description,expiry,hall,date_created,creator,barcode,',
'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/Coupon.php',
'iconfile' => t3lib_extMgm::extRelPath($_EXTKEY) . 'Resources/Public/Icons/tx_bpscoupons_domain_model_coupon.gif'
),
);
t3lib_extMgm::addLLrefForTCAdescr('tx_bpscoupons_domain_model_usedcoupon', 'EXT:bpscoupons/Resources/Private/Language/locallang_csh_tx_bpscoupons_domain_model_usedcoupon.xml');
t3lib_extMgm::allowTableOnStandardPages('tx_bpscoupons_domain_model_usedcoupon');
$TCA['tx_bpscoupons_domain_model_usedcoupon'] = array(
'ctrl' => array(
'title' => 'LLL:EXT:bpscoupons/Resources/Private/Language/locallang_db.xml:tx_bpscoupons_domain_model_usedcoupon',
'label' => 'user',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'dividers2tabs' => TRUE,
'versioningWS' => 2,
'versioning_followPages' => TRUE,
'origUid' => 't3_origuid',
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l10n_parent',
'transOrigDiffSourceField' => 'l10n_diffsource',
'delete' => 'deleted',
'enablecolumns' => array(
'disabled' => 'hidden',
'starttime' => 'starttime',
'endtime' => 'endtime',
),
'searchFields' => 'user,coupon,guest,',
'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/Usedcoupon.php',
'iconfile' => t3lib_extMgm::extRelPath($_EXTKEY) . 'Resources/Public/Icons/tx_bpscoupons_domain_model_usedcoupon.gif'
),
);
?>
If that happens, most probably your objects are not persisted. Normally Extbase persists changes to objects after the end of a request. But if you e.g. return your response as JSON and then exit your action, the persistenceManager is not called.
You can persist manually by injecting the persistenceManager to your controller:
/**
* #var Tx_Extbase_Persistence_Manager
*/
protected $persistenceManager;
/**
* #param Tx_Extbase_Persistence_Manager $persistanceManager
* #return void
*/
public function injectPersistenceManager(Tx_Extbase_Persistence_Manager $persistenceManager) {
$this->persistenceManager = $persistenceManager;
}
and then call it after you added the new object:
$persistenceManager->persistAll();
In TYPO3 4.7+, you can use the #inject annotation in the doc comment of $persistenceManager to inject the persistenceManager and don't need the inject function.
If this doesn't solve the problem, maybe there's a problem with your TCA.

Resources