How to extend tx:cart with a new field in TYPO3? - typo3

I would like to extend the extension cart with a new field to put in the IBAN in the checkout.
So I created a new extension and added a database field with the following code in ext_tables.sql
#
# Table structure for table 'tx_cart_domain_model_order_item'
#
CREATE TABLE tx_cart_domain_model_order_item (
iban varchar(255) DEFAULT '' NOT NULL
);
Now I need to extend the class Item in
ext/cart/Classes/Domain/Model/Order/item.php
I tried to create a file in my extension
ext/cartextend/Classes/Domain/Model/Order/item.php
and tried to extend the class with:
namespace Extcode\Cart\Domain\Model\Order;
use Extcode\Cart\Property\Exception\ResetPropertyException;
class Item extends \Extcode\Cart\Domain\Model\Order
{
/**
* Iban
*
* #var string
*/
protected $iban;
/**
* #return string
*/
public function getIban()
{
return $this->iban;
}
/**
* #param string $iban
*/
public function setIban($iban)
{
$this->iban = $iban;
}
}
I also added an input field that is implemented correctly.
But the IBAN is not saved at all - i guess the extending of the class is wrong.
I really appreciate any hint.
Many thanks! Urs

Maybe you have to extend item.php like this (the rest looks fine):
namespace Extcode\YourExtension\Domain\Model\Order;
class Item extends \Extcode\Cart\Domain\Model\Order\Item
and do not forget to make it known to extbase for you to use iban in the front-end trough typoscript: (I have it to extend cart_products, you'll have to adept it)
config.tx_extbase {
persistence {
classes {
Extcode\CartExtendedProduct\Domain\Model\Product\Product {
mapping {
tableName = tx_cartproducts_domain_model_product_product
recordType =
}
}
}
}
objects {
Extcode\CartProducts\Domain\Model\Product\Product.className = Extcode\CartExtendedProduct\Domain\Model\Product\Product
}
}

Related

Add own sys_category to record with extbase

I made the table of one of my models categorizable which works fine if I set categories in the Backend. If I try to add categories via a frontend form I always get the error:
Call to a member function attach() on null
and have no clue why that is so. Maybe anyone of you can help.
In controller I try to add as usual, find the category
$videoCat = $this->categoryRepository->findByUid(28);
and add it like that
$this->video->addTxVideoCat($videoCat);
That's where the error occurs.
Find below how I added the category to the model.
ext_tables.sql `tx_video_cat int(11) DEFAULT '0' NOT NULL,`
extended tca in TCA/Overrides/sys_template.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::makeCategorizable(
'addvideo',
'tx_addvideo_domain_model_video',
// Do not use the default field name ("categories") for pages, tt_content, sys_file_metadata, which is already used
'tx_video_cat',
array(
// Set a custom label
'label' => 'LLL:EXT:addvideo/Resources/Private/Language/locallang.xlf:video_categories',
// This field should not be an exclude-field
'exclude' => FALSE,
// Override generic configuration, e.g. sort by title rather than by sorting
// string (keyword), see TCA reference for details
'l10n_mode' => 'exclude',
// list of keywords, see TCA reference for details
'l10n_display' => 'hideDiff',
)
);
Created extended category repository
namespace Pixelink\Addvideo\Domain\Repository;
class CategoryRepository extends \TYPO3\CMS\Extbase\Domain\Repository\CategoryRepository
Created extended model
namespace Pixelink\Addvideo\Domain\Model;
class Category extends \TYPO3\CMS\Extbase\Domain\Model\Category
{}
Category mapping in typoscript
plugin.tx_addvideo{
persistence {
classes{
Pixelink\Addvideo\Domain\Model\Category {
mapping {
tableName = sys_category
columns {
}
}
}
}
}
}
and in Model\Video.php I added the following
/**
* txVideoCat
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Pixelink\Addvideo\Domain\Model\Category>
*/
protected $txVideoCat = null;
/**
* Get categories
*
* #return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Pixelink\Addvideo\Domain\Model\Category>
*/
public function getTxVideoCat()
{
return $this->txVideoCat;
}
/**
* Set categories
*
* #param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $txVideoCat
*/
public function setTxVideoCat($txVideoCat)
{
$this->txVideoCat = $txVideoCat;
}
/**
* Add category to a post
*
* #param \Pixelink\Addvideo\Domain\Model\Category $txVideoCat
*/
public function addTxVideoCat(\Pixelink\Addvideo\Domain\Model\Category $txVideoCat)
{
$this->txVideoCat->attach($txVideoCat);
}
You should initialize your property in your Video model constructor:
public function __construct()
{
$this->txVideoCat = new ObjectStorage();
}
Your $this->txVideoCat is null. Use initiailizeObject() method to assign it:
public function initializeObject()
{
$this->txVideoCat = new ObjectStorage();
}

TYPO3 add property and method to the model

I have TYPO3 version 7.6.18, I need add some property to model user
$public age = 0;
and I tried add different methods :
public setAge(){$this->age = 23;}
public age(){return $this->age;}
public getAge(){return $this->age;}
public age(){return 23;}
public getAge(){return 23;}
And on fluid I always get 0. {user.age} - I get 0. What is the problem ? May be because I have not field 'age' in DB table ? But I need add age property to user model without field in DB. Is it possible ? how to do it ?
First things first:
What user model are you talking about?
fe_user, beuser - or something completly different?
How did you tried to add these?
Have you created a custom class which extends the user class?
Greetings,
KamiYang
you must define your getter in this way:
public function getAge()
{
return 23;
}
In your examples you have forgot the function declaration.
If this don't work, check if the correct domain model is gotten.
Try to something like below.
/**
* #var Age
*/
protected $age = 23;
public function __construct($age) {
$this->setAge($age);
}
public function setAge($age) {
$this->age = $age;
}
public function getAge() {
return $this->age;
}

__toString() must return a string value when submitting a form

I have an show view that I had to custom a little bit so we can edit things in it. Among these things there is a multi select that is the result of a query to filter schools I've done inside the controller, sent via the render method.
Before all that, I was using a many-to-many multi select form to select every schools ever saved in the database. Now I want to use it so I can use what's already working.
Since it's sent via the render and not the form, I managed to create an HTML form, to display it, and to get to see what has been selected when I submit the form, however I had several problems:
First of all, it wanted to be an instance of an object, and to be able to save an object instead of an array. I managed to do that by doing the following:
$object = new Ecole();
foreach ($ecolesDispo as $key => $value)
{
$object->$key = $value;
}
$mission->addEcolesDispo($object);
(Ecole is for schools)
The problem I'm now stuck with came right after it, because now it wants it to be converted to string, however, I can't manage to do so.
Here's how the concerned part of my entity looks like.
/**
* Constructor
*/
public function __construct()
{
$this->ecolesDispo = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #return string
*/
public function __toString()
{
return (string) $this->addEcolesDispo($object);
//Not sure about that part though
}
/**
* Add ecolesDispo
*
* #param \EcoleBundle\Entity\Ecole $ecolesDispo
*
* #return Mission
*/
public function addEcolesDispo(\EcoleBundle\Entity\Ecole $ecolesDispo)
{
$this->ecolesDispo[] = $ecolesDispo;
return $this;
}
/**
* Remove ecolesDispo
*
* #param \EcoleBundle\Entity\Ecole $ecolesDispo
*/
public function removeEcolesDispo(\EcoleBundle\Entity\Ecole $ecolesDispo)
{
$this->ecolesDispo->removeElement($ecolesDispo);
}
/**
* Get ecolesDispo
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEcolesDispo()
{
return $this->ecolesDispo;
}
How can I convert this to string?
Thank you in advance
Your __toString function should looks like :
public function __toString()
{
return $this->id; // Because __toString seems to be called to set your $key variable...
}
-> rely on a string property.
In your __toString() function, you use (string) which will implicitely call ... __toString() to convert $this to a string. That would be a circular call.
Try this if there's a string variable name in the entity where you were using __toString or use any string type property of that entity which specifies the entity itself.
public function __toString()
{
// Or change the property that you want to show
return $this->name;
}

[Symfony][Form] Add validator/constraint to property only if it has changed

I've got the following scenario: I'm validating appointments and there's a custom validator, which tells the user if his choosen date is valid or not. It's not valid, if the date is already blocked by another entity. This works flawlessly on adding new entities.
Now I'd like to trigger the date validation on edit only if the date itself has changed. So just changing the title of the appointment should not validate the date.
My entity class:
use Doctrine\ORM\Mapping as ORM;
use Acme\Bundle\Validator\Constraints as AcmeAssert;
/**
* Appointment
*
* #ORM\Entity
* #AcmeAssert\DateIsValid
*/
class Appointment
{
/**
* #ORM\Column(name="title", type="string", length=255)
*
* #var string
*/
protected $title;
/**
* #ORM\Column(name="date", type="date")
*
* #var \DateTime
*/
protected $date;
}
The validator class (used as a service):
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the date of an appointment.
*/
class DateIsValidValidator extends ConstraintValidator
{
/**
* {#inheritdoc}
*/
public function validate($appointment, Constraint $constraint)
{
if (null === $date = $appointment->getDate()) {
return;
}
/* Do some magic to validate date */
if (!$valid) {
$this->context->addViolationAt('date', $constraint->message);
}
}
}
The corresponding Constraint class is set to target the entity class.
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class DateIsValid extends Constraint
{
public $message = 'The date is not valid!';
/**
* {#inheritdoc}
*/
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
/**
* {#inheritdoc}
*/
public function validatedBy()
{
return 'acme.validator.appointment.date';
}
}
Now I don't find a clean way to depend on a date change. I could simply track the old date in my entity, but that doesn't feel like a proper solution, if I'd like to implement more complex constraints. :[
Cheers
Since symfony 2.3 you can use Form Events to solve this problem. I added the change-check code to my FormType, by storing (and cloning) the original entity at the form creation.
Then added a POST_SUBMIT event listener to check if the fields were changed. The listener can add validation errors to your fields.
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormError;
use Acme\Bundle\Entity\Appointment;
class AppointmentType extends AbstractType
{
private $originalAppointment;
public function __construct(Appointment $original)
{
// save the original entity
$this->originalAppointment = clone $original;
}
// ...
public function buildForm(FormBuilderInterface $builder, array $options)
{
// define your fields
$builder->addEventListener(FormEvents::POST_SUBMIT, [$this, 'dateCheckListener']);
}
public function dateCheckListener(FormEvent $event)
{
$appointment = $event->getData();
$form = $event->getForm();
// if no appointments exist, we can skip the check
if (empty($appointment) || empty($this->originalAppointment)) {
return;
}
if ($appointment->getDate() !== $this->originalAppointment->getDate()) {
// the dates changed, you can call your validator here
if ('dates are not valid') {
$form->get('date')->addError(new FormError('We have a problem.'));
}
}
}
}
In your controller, you can create this formType with the original appointment:
$appointment = $this->getYourAppointmentSomehow();
$form = $this->createForm(new AppointmentType($appointment), $appointment);
Maybe you will find this article useful, to check which property is changed. Everything is possible in symfony. You might end up writing entity listeners, listener resolvers and so on. Things can get ultra advanced.
http://docs.doctrine-project.org/en/latest/reference/change-tracking-policies.html
Pay attention to the setter method:
public function setData($data)
{
if ($data != $this->data) {
$this->_onPropertyChanged('data', $this->data, $data);
$this->data = $data;
}
}
Do you see the trick?:)
I would also use !== operator to also check variable type.
You can also simplify things. You dont need to call _onPropertyChanged, but call the function, which will set a property 'dateChanged' to true. Then use method:
public function getGroupSequence()
{
if($this->dateChanged)
{
return ['date_check'];
}
else
{
return false;
}
}
And also tell your class that it implements GroupSequenceProviderInterface.
You can then use the validation group in your validation.yml for example.
maybe you want to try it with a preUpdate-Listener instead of a custom validation constraint?
Section 10.5.4 in the doctrine documentation gives an example of a validation listener "ValidCreditCardListener".
i know this will not work for automagic form validation, but i think it's the fastest way atm.
edit:
another option could be to use #UniqueEntiy constraint for the date field of your Appointment class. this will not break form validation but will cause an additional database query (as far as i know)

select queries with Zend_DB_Table

I have a code something like following
class Application_Model_Company extends Zend_Db_Table_Abstract {
protected $_name = 'companies';
private $id;
private $name;
private $shortName;
private $description;
public static function getAllCompanies() {
$companyObj = new self();
$select = $companyObj->select()->order(array('name'));
$rows = $companyObj->fetchAll($select);
if($rows) {
$companies = array();
foreach($rows as $row) {
$company = new self();
$company->id = $row->id;
$company->name = $row->name;
$company->shortName = $row->short_name;
$company->description = $row->description;
$companies[] = $comapny;
}
// return Company Objects
return $companies;
}else
throw new Exception('Oops..');
}
}
I need to return Company Objects from getAllCompanies() function, But it returns Zend_Db_Table_Row Object. How do I correct this?
Your Model class shouldnt extend the table class. The table class is separate. Your Model should extend the row class if extending anything from Zend_Db at all. Also you shouldnt put retrieval methods on your Model classes directly, they would go on the table classes.
This is because in the paradigm youre trying to use here, a Model represents a single Row of data, the Table class represents the table as a repository of data, and the Rowset class represents a collection of Rows (or Models).
To properly implement what you are describing in your question you would do something like the following:
class Application_Model_DbTable_Company extends Zend_Db_Table_Abstract
{
// table name
protected $_name = 'company';
protected _$rowClass = 'Application_Model_Company';
// your custom retrieval methods
}
class Application_Model_Company extends Zend_Db_Table_Row
{
protected $_tableClass = 'Application_Model_DbTable_Company';
// custom accessors and mutators
}
However, using some kind of implementation of the Data Mapper pattern is whats actually recommended. Check out the Quickstart for a thorough tutorial on a simplified implementation.