Selected value based on ID in EntityType - symfony-3.4

I want the EntityType (DefaulList) to choose the default value based on the ID from the Data entity.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('mpkId', EntityType::class, array(
'class' => MpkList::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.id', 'ASC');
},
'choice_label' => 'mpk'
))
->add("time", IntegerType::class)
->add("submit", SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(["data_class"=>Data::class]);
}
example: Data['mpkId']=1, MpkList['id'=>1,'mpk'=>'description']. Selected field in form 'description'

You can use the callback function . And access to all objects in your MpkList Class.
something like this
->add('mpkId', EntityType::class, array(
'class' => MpkList::class,
'choice_label' => function (MpkList $MpkList) {
return "id=".$MpkList->getId()."-Description=". $MpkList->getMpkId()->getDescription();
},
))

Related

is_user_logged_in() return false even when logged in to WordPress?

I have a plugin that I created and I want to use the WP rest api controller pattern and extend the api.
<?php
/**
* Plugin Name: myplugin
* Plugin URI: h...
* Description: A simple plugin ...
* Version: 0.1
* Author: Kamran ...
* Author ....
* License: GPL2
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items();
$items->register_routes();
}
add_action('rest_api_init','myplugin_register_endpoints');
.
.
I created a class in folder called server/controllers and inside it my_ctrl.php file with a class that extends WP_REST_Controller that looks like this
<?php
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
$version = '1';
$namespace = 'my-namespase/v' . $version;
$base = 'abc';
register_rest_route( $namespace, '/' . $base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
) );
register_rest_route( $namespace, '/' . $base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
'args' => array(
'id' => array(
'required' => true,
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param ) and ! is_null(get_post($param));//numeric post id value and there is valid post for this id
},
'sanitize_calback' => 'absint'
)
),
),
) );
register_rest_route( $namespace, '/' . $base . '/schema', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_public_item_schema' ),
) );
}
function get_items( $request ){
return new WP_REST_Response( array('message' => "list items"), 200 );
}
function create_item( $request ) {
.....
if($author_email==$user_email) {
return new WP_REST_Response( array('message' => 'success', 200 );
} else {
return new WP_Error('my-error', __(' error...','abc'), array( 'status' => 500 ));
}
}
//Remove vote////////////////////////////////////////////
function delete_item( $request ) {
...
if($author_email==$user_email) {
return new WP_REST_Response( array('message' => 'success', 200 );
} else {
return new WP_Error('my-error', __(' error...','abc'), array( 'status' => 500 ));
}
}
public function get_items_permissions_check( $request ) {
return true;
}
public function create_item_permissions_check( $request ) {
if ( !is_user_logged_in()) {
return new WP_Error('login error',__('You are not logged in','KVotes-voting'));
}
return true;
}
public function delete_item_permissions_check( $request ) {
return $this->create_item_permissions_check( $request );
}
protected function prepare_item_for_database( $request ) {
return array();
}
public function prepare_item_for_response( $item, $request ) {
return array();
}
public function get_collection_params() {
return array(
'page' => array(
'description' => 'Current page of the collection.',
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
),
'per_page' => array(
'description' => 'Maximum number of items to be returned in result set.',
'type' => 'integer',
'default' => 10,
'sanitize_callback' => 'absint',
),
'search' => array(
'description' => 'Limit results to those matching a string.',
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
);
}
}
I am logged in and I am using cookie authentication with Nonce in my plugin.
when I run my code and debug it with sublime xdebug extension I can see that I indeed hit the end points routes but although I am logged it in the lines: "is_user_logged_in()" = (bool) 0 and therefore the function create_item_permissions_check return new WP_Error(....);and not true;
therefore my rest callback "create_item" is not invoked, I don't understand why is_user_logged_in() return false even when I am logged in.
The solution was to send the logged in user info to my custom class as a parameter to the constructor and then use the user data in the permission check function and other functions that needs the user info:
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
private $loged_in;//bool
private $user;
public function __construct($logged,$cur_user) {
= $logged;
$this->user = $cur_user;
}
.
.
.
public function create_item_permissions_check( $request ) {
if($this->loged_in!=1){
return new WP_Error('login error',__('You are not logged in','....'));
}
return true;
}
.
.
.
}
And my plugin myplugin_register_endpoints looks as follows:
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items(is_user_logged_in(),wp_get_current_user());
$items->register_routes();
}
now when I route to one of the URL's And hit the end points and the check permission is invoked with the needed user data. $this->loged_in!=1 when thew user is not logged in, otherwise the permission check returns true .
I ran into precisely this issue. It seems that the architecturally correct way to handle the problem is for the ajax request to include a nonce. See the references discussed in this answer

Filters in CGridView not filtering

Can You check why filtering is not working in CGridView? When i type for exaple 'Adam' in filter field, nothing happens. I can't find my mistake, everything looks ok but not working. I helped with that article: Yii: CGridView Filter examples
CONTROLLER
<?php
class UzytkownikController extends CController
{
public function actionIndex()
{
$Dane = new Uzytkownik('search');
$Dane -> unsetAttributes(); // clear any default values
if(isset($_GET['Uzytkownik']))
{
$Dane->attributes=$_GET['Uzytkownik'];
}
$this -> render ('index', array(
'Dane' => $Dane,
));
}
}
?>
MODEL
<?php
class Uzytkownik extends CActiveRecord
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public function search()
{
$criteria = new CDbCriteria;
$criteria -> compare('imie', $this -> imie, true);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
)
);
}
}
?>
WIEV
<?php
$this -> widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $Dane -> search(),
'filter' => $Dane,
'columns' => array(
array(
'name' => 'imie',
'type'=>'raw',
),
array(
'name' => 'nazwisko',
'type'=>'raw',
'filter' => false,
),
array(
'name' => 'data',
'filter' => false,
),
),
)
);
?>
For future reference:
In order to make sure the $model->attributes "saves" the attributes the model needed the following addition:
public function rules() {
return array(
array('imie', 'safe', 'on'=>'search')
);
}
And $_GET should have been used instead of $_POST Because the CGridView widget uses GET when posting to the server:
class UzytkownikController extends CController
{
public function actionIndex()
{
$Dane = new Uzytkownik('search');
$Dane -> unsetAttributes(); // clear any default values
if(isset($_GET['Uzytkownik']))
{
$Dane->attributes=$_GET['Uzytkownik'];
}
$this -> render ('index', array(
'Dane' => $Dane,
));
}
}

FOSRestBundle post many to one relation

I would like to know how to properly post data when Entity has another ManyToOne relation in FOSRestBundle.
User entity has locale (locale_id):
/**
* #ORM\ManyToOne(targetEntity="Locale")
* #ORM\JoinColumn(name="locale_id", referencedColumnName="id")
*/
private $locale;
I was hoping that passing something like:
{
"user":{
"firstName":"John",
"emailAddress":"somewhere#somehow.com",
"lastName":"Doe",
"sex":"1",
"locale":{
"id":"1"
}
}
}
will work, but it does not pass the validation and Symfony throws:
{"code":400,"message":"Validation Failed","errors":{"children":{"firstName":[],"lastName":[],"emailAddress":[],"sex":[],"locale":{"errors":["This value is not valid."]}}}}
As you can see, locale is still wrong.
Does anyone know how can I post it properly?
EDIT
Here is how the form looks like:
<?php
namespace Software\Bundle\Form\Type;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class UserType
* #package Software\Bundle\Form\Type
*/
class UserType extends AbstractFormType
{
public function buildForm(FormBuilderInterface $builder, array $option)
{
$builder
->add('firstName', 'text', [
'label' => 'word.first_name',
'required' => true
])
->add('lastName', 'text', [
'label' => 'word.last_name',
'required' => true
])
->add('emailAddress', 'email', [
'label' => 'word.email_address',
'required' => true
])
->add('sex', 'choice', [
'label' => 'word.sex',
'choices' => [
'0' => 'word.male',
'1' => 'word.female'
],
'required' => true,
'empty_value' => 'word.select',
'empty_data' => null
])
->add('locale', 'entity', [
'label' => 'word.locale',
'required' => false,
'property' => 'code',
'class' => 'SoftwareBundle:Locale',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('l')
->orderBy('l.code', 'ASC');
},
'placeholder' => 'word.select',
'empty_data' => null
])
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'translation_domain' => 'general',
'data_class' => 'Software\Bundle\Entity\User',
'attr' => ['novalidate' => 'novalidate'],
'csrf_protection' => false
]);
}
public function getName()
{
return 'user';
}
}
EDIT 2
And the controller:
public function postAction(Request $request)
{
$form = $this->createForm(new UserType(), new User());
$form->handleRequest($request);
if($form->isValid())
{
die('are you valid or not??');
}
return $this->view($form, 400);
}
Try without the "1" and only with 1 , otherwise it can be interpreted as string.
Edit :
{
"user":{
"firstName":"John",
"emailAddress":"somewhere#somehow.com",
"lastName":"Doe",
"sex":"1",
"locale": 1
}
}
}

Symfony Forms Neither the property "Category" nor one of the methods "getCategory()" error

I keep getting the following error:
Neither the property "Category" nor one of the methods
"getCategory()", "category()", "isCategory()", "hasCategory()",
"__get()" exist and have public access in class
"Test\TesterBundle\Model\Products"
I've got 3 tables: Products, Category and ProductCategories. The ProductCategories is the ID of the Producs and Category Table (Many to Many). But I keep getting the error above.
I've got the following form builders:
class ProductsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('ProductName');
$builder->add('ProductDescription', 'textarea', array("required"=> false));
$builder->add('ShortDescription', null);
$builder->add('SKU', null);
$builder->add('UnitWeight', null);
$builder->add('UnitPrice', null);
$builder->add('UnitLength', null);
$builder->add('UnitHeight', null);
$builder->add('UnitDepth', null);
$builder->add('URL', null);
$builder->add('MetaTitle', null);
$builder->add('MetaDescription', null);
$builder->add('MetaKeywords', null);
$builder->add('Category', 'collection', array(
'type' => new \Test\TesterBundle\Form\Type\CategoryType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'validation_groups' => array('newProducts'),
'data_class' => 'Test\TesterBundle\Model\Products',
));
}
public function getName(){
return "Products";
}
}
And The second being:
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('CategoryName', 'model', array(
'class' => 'Test\TesterBundle\Model\Productcategory',
'required' => false,
'multiple' => true,
'expanded' => false,
'property' => 'label'
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Test\TesterBundle\Model\Productcategory'
));
}
public function getName(){
return "ProductCategory";
}
}
Any thoughts on what would be causing the issue? I'm not sure if it's the instance that is used in the controller or if it is because I've setup the form builders incorrectly and cannot get a direct relation to the Category field (which it won't). If I use mapped => false, then it works however the builder has to populate the select box from the database, which settings mapped to false stops it doing this.

How to use the InputFilter in a nested mapper model class in Zend Framework 2?

Everyone, who started ZF2 learning with the "Get started" tutorial, will know the model class Album (s. below).
Now I want to extend my model with songs. One album can have 0 or more songs. The songs will get a new talbe songs (id, title, album_id) and the mapper Album\Model\Song. The mapper Album\Model\Song will be built similar to Album\Model\Album. The mapper Album\Model\Album will get a new property songCollection (array of Album\Model\Song objects or maybe something like Album\Model\SongCollection object).
Does it make sence to use the InputFilter for "nested" (mapper) classes?
How should the getInputFilter() be modified?
How should the setInputFilter() be modified? OK, now it is not implemented at all. But it's approximately clear how to do it for a shallow class structure -- and not clear how to implement it for a mapper, that references another mapper(-s).
Album\Model\Album
<?php
namespace Album\Model;
use Zend\Stdlib\ArraySerializableInterface;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Album implements InputFilterAwareInterface, ArraySerializableInterface {
public $id;
public $artist;
public $title;
protected $inputFilter;
public function exchangeArray(array $data) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
public function toArray() {
return $this->getArrayCopy();
}
public function getArrayCopy() {
return get_object_vars($this);
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception('Not used');
}
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int')
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'artist',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validarots' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100
)
)
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validarots' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100
)
)
)
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
I think you are a little confused with the relationship with the models and mappers set out in this example.
The 'mappers' would be the TableGateway objects, such as AlbumTable, SongTable etc. The Album and Song classes yo would call models, or Domain Objects, these are what represent the actual entities in your application. The Mappers just take care of persisting them in your database etc.
When using the TableGateway implementation, I would let each Domain Object (such as Ablum) handle the InputFilter for the attributes it's TableGateway is going to persist (such as AlbumTable).
For the example you stated, I would not change the Album Models InputFilter at all. The reason is the relationship with Songs is this:
Album HAS many songs, Song Belongs to Album (the Song would have the link back to the Album)
Add a new Song Object and Gateway:
<?php
namespace Album\Model;
use Zend\Stdlib\ArraySerializableInterface;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Song implements InputFilterAwareInterface, ArraySerializableInterface {
protected $id;
protected $album;
protected $title;
protected $inputFilter;
// Added Getters / Setters for the attributes rather than
// having public scope ...
public function setAlbum(Album $album)
{
$this->album = $album;
}
public function getAlbum()
{
return $this->album;
}
public function exchangeArray(array $data) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
if(isset($data['album_id'])) {
$album = new Album();
$album->exchangeArray($data['album_id']);
$this->setAlbum($album);
}
}
public function toArray() {
return $this->getArrayCopy();
}
public function getArrayCopy() {
return array(
'id' => $this->id,
'album_id' => $this->getAlbum()->id,
'title' => $this->title,
);
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception('Not used');
}
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int')
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'album_id',
'required' => true,
'filters' => array(
array('name' => 'Int')
)
)));
$inputFilter->add($factory->createInput(array(
'name' => 'title',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validarots' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100
)
)
)
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
Notice no need to change the Album Model as the relationship is 'Song Belongs to Album'.
When you object relationships get more complex you will want to look at using Hydrators to build the objects for you (http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.html)
Now you would create a SongTable to persist this new Object for you:
<?php
namespace Album\Model;
use Zend\Db\TableGateway\TableGateway;
class SongTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getSong($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function saveSong(Song $song)
{
$data = array(
'album_id' => $song->getAlbum()->id,
'title' => $song->title,
);
$id = (int)$song->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getSong($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function fetchAlbumSongs(Album $album)
{
$resultSet = $this->tableGateway->select(array(
'album_id' => $album->id
));
return $resultSet;
}
public function addSongsToAlbum(Album $album)
{
foreach($this->fetchAlbumSongs($album) as $song) {
$album->addSong($song);
}
}
}
You Could then Modify you Album model to allow Songs to be added:
class Album implements InputFilterAwareInterface, ArraySerializableInterface {
// Other stuff here
/**
* #var array
*/
protected $songs = array();
public function addSong(Song $song)
{
$this->songs[] = $song;
}
public function getSongs()
{
return $this->songs;
}
}
You can then build your object graph easily, I would usually make a server to do do this kind of thing:
AlbumService.php
public function getAlumbWithSongs(int $id)
{
$album = $this->getAlbumTable()->getAlbum($id);
if($album) {
$this->getSongTable()->addSongsToAlbum($album);
}
return $album;
}