i have a problem that make my head blow about eloquent went to the wrong table here my code for my activity model
class Activity extends Model{
use HasFactory;
protected $table = 'activities';
protected $guarded = ['id'];
public function getRouteKeyName()
{
return 'slug';
}
and the problem is when i store to the database, eloquent went to the wrong table
here is my store method
public function store(StoreActivityRequest $request)
{
$validatedData = $request->validate([
'name' => 'required',
'slug' => 'required|unique:activies'
]);
Activity::create($validatedData);
return redirect('/activities')->with('success', 'Tindakan Berhasil Ditambahkan');
}
this is my customrequest (actually my laravel make it by default)
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreActivityRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
here is the message
Illuminate\Database\QueryException
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'klinik_app.activies' doesn't exist (SQL: select count(*) as aggregate from `activies` where `slug` = konsultasi)
its go to activies table... if i change the tables on migrate file to activies, then the other method gone wrong because it's can't found activities. and this problem just happen to my store method, another method just do the right thing... is there any body can help me?
Update: Added from comments:
public function store(StoreActivityRequest $request) {
$validatedData = $request->validate([
'name' => 'required',
'slug' => 'required|unique:activies'
]);
Activity::create($validatedData);
return redirect('/activities')->with('success', 'Tindakan Berhasil Ditambahkan');
}
Related
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();
}
I need a validator that needs to query database. This means I have to create a Constraint a ConstraintValidator and setup a service to inject EntityManager.
I did it, and researched official documentation and lots of posts and couldn't make it work. It seems that validatedBy() is not able to start the service (injecting then the EntityManager).
Here is the code I'm struggling with:
services.yml
validator.frontend.class:
class: Project\UsersBundle\Validation\Constraints\ConstrainsActiveValidator
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: validator.constrain_validator, alias: the_alias }
ConstrainsActive.php
namespace Project\UsersBundle\Validation\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
* */
class ConstrainsActive extends Constraint
{
public function validatedBy()
{
return 'the_alias'; // get_class($this).'Validator';
}
}
ConstrainsActiveValidator.php
namespace Project\UsersBundle\Validation\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Doctrine\ORM\EntityManager;
class ConstrainsActiveValidator extends ConstraintValidator
{
protected $em;
public function __construct(EntityManager $v)
{
$this->em = $em;
}
public function validate($value, Constraint $constraint)
{
$this->context->buildViolation('This name sounds totally fake2!')
->atPath('useEmail')
->addViolation();
}
}
Update:
Thanks for the typo issue. It was a mistake when adapting code to posting here. I fixed! :)
Here you are the error I'm always getting:
Attempted to load class "the_alias" from the global namespace in /var/www/Project/current/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php line 71. Did you forget a use statement for this class?
I temporally edited the file ConstraintValidatorFactory.php, and did a var_dump for the var $this->validators and get the response:
array (size=4)
'validator.expression' => string
'validator.expression' (length=20)
'Symfony\Component\Validator\Constraints\EmailValidator' => string
'validator.email' (length=15) 'security.validator.user_password' =>
string 'security.validator.user_password' (length=32)
'doctrine.orm.validator.unique' => string
'doctrine.orm.validator.unique' (length=29)
It seems like symfony framework didn't consider the alias "the_alias" set up in my custom service validator.frontend.class, because it didn't come in the var_dump.
I hope this can give you some clue. Thanks!
You code is ok, check the typo in the construct method where you name the EntityManager as $v and ferer it as $m.
Change the construct of ConstrainsActiveValidatorclass as:
public function __construct(EntityManager $v)
{
$this->em = $v;
}
What error do you have?
Just for people that has the same issue. I finally decided to set checking form parameters inside a type, I mean:
Bundle/Form/addType.php
adding an event listener to the $builder object:
public function buildForm(FormBuilderInterface $builder, array $options) {
...
$builder->addEventListener(FormEvents::POST_SUBMIT, function ($event) {
$data = $event->getData();
$form = $event->getForm();
if (null === $data) return;
// Checking comes here!!!
// ......
}
}
In my opinion, validators are great but when a project gets bigger, checking information inside the event is the best way. You can also use some constraints that provide symfony as EmailConstraints:
use Symfony\Component\Validator\Constraints\Email as EmailConstraint;
Answering myself. Let's validate that an entered slug for a post in a blog is unique:
Let's assume we have a bundle called AppBundle
1) Create the subfolder structure "Validator/Contraints" inside src/AppBundle.
2) Inside .../Validator/Constraints, create a file called SlugUnique.php (The constraint):
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class SlugUnique extends Constraint
{
public $message = 'Slug "{{ slug }}" already taken';
public function getTargets()
{
return array(self::PROPERTY_CONSTRAINT);
}
public function validatedBy()
{
return 'app.validator.blog.slug_unique';
}
}
3) Create a file called SlugUniqueValidator.php at the same folder level (The constraint validator):
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Doctrine\ORM\EntityManager;
use AppBundle\Entity\Blog as BlogEntity;
/**
* #Annotation
*/
class SlugUniqueValidator extends ConstraintValidator
{
/** #var EntityManager */
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function validate($value, Constraint $constraint)
{
/** #var BlogEntity $blogEntity */
$blogEntity = $this->context->getRoot()->getViewData();
if (null !== $this->em->getRepository('AppBundle:Blog')->findBy(['slug' => $blogEntity->slug])) {
$this->context->buildViolation($blogEntity->slug)
->atPath('slug')
->addViolation();
}
}
}
4) Create an entry into services.yml with properly tag items:
app.validator_constraints_blog.slug_unique_validator:
class:AppBundle\Validator\Constraints\SlugUniqueValidator
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: 'validator.constraint_validator', alias: 'app.validator.blog.slug_unique' }
Note that the value for the alias parameter must match the value returned by validatedBy in the SlugUnique class constraint.
5) Finally, assign such validation to the corresponding entity field:
namespace AppBundle\Entity;
use AppBundle\Validator\Constraints as ValidatorConstraints;
class Blog
{
...
/**
* #var string
*
* #ORM\Column(name="slug", type="string", length=255, nullable=false)
* #ValidatorConstraints\SlugUnique
*/
private $slug;
public getSlug()
...
Notice that depending on the Entity fieldName and the form fieldName, that you may have to set the parameter 'property_path' when building the form type. Example:
$builder->add(
'slug', TextType::class,
array('required' => false, 'property_path' => 'slug')
);
Please note that you can inject a custom service into SlugUniqueValidator instead on the EntityManager for better separation of concerns and bests practices.
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)
I am having problems getting my doctrine hydrator to hydrate my return post forms.
Post forms.
I keep getting the following message:
An exception occurred while executing 'INSERT INTO worker_essay
(title) VALUES (?)' with params [null]: SQLSTATE[23000]: Integrity
constraint violation: 1048 Column 'title' cannot be null
but this cannot be correct because I have a validator on my form requiring this value to be inserted, yet my form is validating.
I would really appreciate any help or advice on resolving the problem or advice on how to go about discovering what is causing the problem.
public function getInputFilterSpecification()
{
return array(
'title' => array(
'required' => true
),
);
}
these are the var_dumped values from the returned form:
object(Zend\Stdlib\Parameters)[146] public 'WorkerStatement' =>
array (size=2)
'id' => string '' (length=0)
'title' => string 'the values from title' (length=21) public 'submit' => string 'Submit' (length=6)
As you can see, the values are clearly there, which means that the problem might be in the hydrators.
I now enclosed the rest of the documents.
The Controller
public function workerStatementAction()
{
$form = new CreateWorkerStatementForm($this->getEntityManager());
$workerStatement = new WorkerStatement();
// $form->setInputFilter($workerEssay->getInputFilter());
$form->bind($workerStatement);
// var_dump($workerStatement); die();
if ($this->request->isPost()) {
$post = $this->request->getPost();
$form = $form->setData($this->request->getPost());
if ($form->isValid()) {
$post =$this->request->getPost();
$this->getEntityManager()->persist($workerStatement);
$this->getEntityManager()->flush();
// Redirect to list of aboutyou
return $this->redirect()->toRoute('worker');
}
}
return array('form' => $form);
}
The fieldset
class WorkerStatementFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('WorkerStatement');
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement'))
->setObject(new WorkerStatement());
$this->add(array(
'name' => 'title',
'type' => 'Zend\Form\Element\Text',
'options' => array(
'label' => 'title',
),
));
}
** The Form**
class CreateWorkerStatementForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('WorkerStatement');
// The form will hydrate an object of type "AboutYou"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement'));
// Add the user fieldset, and set it as the base fieldset
$workerStatementFieldset = new WorkerStatementFieldset($objectManager);
$workerStatementFieldset->setUseAsBaseFieldset(true);
$this->add($workerStatementFieldset);
}
}
Here is the var_daump of the persist in the controller:
$this->getEntityManager()->persist($workerStatement);
object(Workers\Entity\WorkerStatement)[351]
protected 'id' => null
protected 'title' => null
You will note that they are empty, yet the var dump of the values from the returned post clearly contain the values.
I enclose my workstatement class. you will note that I have used the magic getter/setter.
<?php
namespace Workers\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
*
* #ORM\Entity
* #ORM\Table(name="worker_essay")
* #property string $title
*/
class WorkerStatement
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* Magic getter to expose protected properties.
*
* #param string $property
* #return mixed
*/
public function __get($property)
{
return $this->$property;
}
/**
* Magic setter to save protected properties.
*
* #param string $property
* #param mixed $value
*/
public function __set($property, $value)
{
$this->$property = $value;
}
public function getInputFilterSpecification()
{
return array(
'title' => array(
'required' => true
)
);
}
}
DoctrineHydrator by default is hydrating and extracting values using getters and setters. If your entity doesn't have these methods then it cannot work properly. If you dont' want to use getters/setters, use new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement', false) instead of new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement').
Maybe it's not the reason why hydrator doesn't work. Please edit your first post and paste Workers\Entity\WorkerStatement class.
EDIT
Hydrator is calling getTitle() and your magic method is trying to access getTitle property which doesn't exist. You have three options:
Change DoctrineHydrator to new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement', false).
Add getters and setters. For example getTitle(), setTitle($title).
Refactor magic methods to accept getProperty, setProperty.
Actually You dont need to add the hydrator in the form , use it in the controller (or service) if its necessary .
plz add a var dump before :
$this->getEntityManager()->persist($workerStatement);
and post the result
I have class that representing model of user with foreign key with is id of picture .
class Model_User extends Model_AbstractEntity
{
protected $u_id;
protected $u_email;
protected $u_firstname;
protected $u_lastname;
protected $u_password;
protected $u_salt;
protected $u_created_at;
protected $u_updated_at;
protected $u_fb;
protected $u_status;
protected $u_r_id;
protected $u_p_id;
}
Class with is responsible for picture model look like this:
class Model_Picture extends Model_AbstractEntity
{
protected $p_id;
protected $p_created_at;
protected $p_updated_at;
protected $p_caption;
protected $p_name;
protected $p_basePath;
protected $p_available;
protected $p_u_id;
}
This is only model part with is getting data from database.
Foreing key is u_p_id and key in picture is p_id
My problem is that when doing select() by Zend db table it returning me data with foreign key but how can I know which part of return data is picture part to set the proper picture model.... how to do it in proper way no to do 2 queries one for user and second for picture to create 2 associative objects.
I'm talking now about relation ont to one but maybe will be one to many..
Typically your entity models will not exist in void they will exist in concert with some type of Data Mapper Model. The Mapper will typically be charged with gathering the data from whatever source is handy and then constructing the entity model.
For example I have a music collection that has an album entity:
<?php
class Music_Model_Album extends Model_Entity_Abstract implements Interface_Album
{
//id is supplied by Entity_Abstract
protected $name;
protected $art;
protected $year;
protected $artist; //alias of artist_id in Database Table, foreign key
protected $artistMapper = null;
/**
* Constructor, copied from Entity_Abstract
*/
//constructor is called in mapper to instantiate this model
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
}
}
/**
* Truncated for brevity.
* Doc blocks and normal getters and setters removed
*/
public function getArtist() {
//if $this->artist is set return
if (!is_null($this->artist) && $this->artist instanceof Music_Model_Artist) {
return $this->artist;
} else {
//set artist mapper if needed
if (!$this->artistMapper) {
$this->artistMapper = new Music_Model_Mapper_Artist();
}
//query the mapper for the artist table and get the artist entity model
return $this->artistMapper->findById($this->getReferenceId('artist'));
}
}
//set the artist id in the identity map
public function setArtist($artist) {
//artist id is sent to identity map. Can be called later if needed - lazy load
$this->setReferenceId('artist', $artist);
return $this;
}
//each album will have multiple tracks, this method allows retrieval as required.
public function getTracks() {
//query mapper for music track table to get tracks from this album
$mapper = new Music_Model_Mapper_Track();
$tracks = $mapper->findByColumn('album_id', $this->id, 'track ASC');
return $tracks;
}
}
In the mapper I would build the entity model like:
//excerpt from Model_Mapper_Album
//createEntity() is declared abstract in Model_Mapper_Abstract
public function createEntity($row)
{
$data = array(
'id' => $row->id,
'name' => $row->name,
'art' => $row->art,
'year' => $row->year,
'artist' => $row->artist_id,//
);
return new Music_Model_Album($data);
}
to use this method in a mapper method, might look like:
//this is actually from Model_Mapper_Abstract, b ut give the correct idea and will work in any of my mappers.
//this returns one and only one entity
public function findById($id)
{
//if entity id exists in the identity map
if ($this->getMap($id)) {
return $this->getMap($id);
}
//create select object
$select = $this->getGateway()->select();
$select->where('id = ?', $id);
//fetch the data
$row = $this->getGateway()->fetchRow($select);
//create the entity object
$entity = $this->createEntity($row);
//put it in the map, just in case we need it again
$this->setMap($row->id, $entity);
// return the entity
return $entity;
}
I have seen Entities and Mappers built in many different ways, find the method that you like and have fun.
A lot of code has been left out of this demonstration as it doesn't really apply to the question. If you need to see the complete code see it at GitHub.