Symfony widget clear label programmatically on render - php

I am trying to programmatically set the label value of a widget after performing some checks but my below attempts are not working. Can anyone see what I am doing wrong? Note that the label already has a value and I just want to clear it. That is in symfony 1.4.
class customFormSome extends sfWidgetFormTextarea {
/**
* Constructor.
*
* #see sfWidgetFormTextarea
* */
protected function configure($options = array(), $attributes = array()) {
$this->addOption('element_id');
}
/**
* #param string $name The element name
* #param string $value The value displayed in this widget
* #param array $attributes An array of HTML attributes to be merged with the default HTML attributes
* #param array $errors An array of errors for the field
*
* #see sfWidget
* */
public function render($name, $value = null, $attributes = array(), $errors = array()) {
/*** SOME PROCESSING HERE******/
$this->setAttribute('label', ''); //---->DOESNT WORK
$this->setAttribute('label', FALSE); //---->DOESNT WORK
$this->setAttribute('label', NULL); //---->DOESNT WORK
$fields = $this->parent->setLabel($this->getOption('element_id'), '');//---->DOESNT WORK
}

It's too late to call setAttiribute() in render method, it gets attributes from $attributes parameter, so you only need to overwrite it:
public function render($name, $value = null, $attributes = array(), $errors = array()) {
/*** SOME PROCESSING HERE******/
$attributes['label'] = $this->getOption('element_id');
return parent::render($name, $value, $attributes, $errors);
}

Related

Filtering collections in api-platform with DataProvider maintaining pagination

We implemented a RolePermissionChecker, which should control the security/accessibility of a specific resource (Offer) comparing attributes of the accessor and creator of the resource. For itemOperations we are using a Voter (https://api-platform.com/docs/core/security/#hooking-custom-permission-checks-using-voters) which is working perfectly.
According to https://api-platform.com/docs/core/security/#filtering-collection-according-to-the-current-user-permissions for collectionOperations DataProvider should be used - so we implemented one.
The issue we got now is that the OfferCollectionDataProvider firstly calls its parent ApiPlatform\Core\Bridge\Doctrine\Orm\CollectionDataProvider::getCollection function in order to apply all extensions and retrieve the collections. Afterwards we filter the entities the user has no access to read. But as we are using pagination and filtering on a ready page, the pagination is broken afterswards as for example half of the page entries will be filtered and for the user it looks like there is no more data.
Does anybody have a good idea how to filter collections without breaking pagination and without using extensions as our permissions checks are a bit complex and very hard to convert into queries?
<?php
namespace App\DataProvider;
use ApiPlatform\Core\Bridge\Doctrine\Orm\CollectionDataProvider;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
use App\Entity\Offer;
use App\Entity\User;
use App\Service\RolePermissionChecker;
use ArrayIterator;
use Doctrine\Persistence\ManagerRegistry;
use ReflectionProperty;
use Symfony\Component\Security\Core\Security;
/**
* Class OfferCollectionDataProvider
*
* #package App\DataProvider
*/
class OfferCollectionDataProvider extends CollectionDataProvider
{
/**
* #var RolePermissionChecker
*/
protected RolePermissionChecker $rolePermissionChecker;
/**
* #var Security $security
*/
protected Security $security;
/**
* #param RolePermissionChecker $rolePermissionChecker
* #param Security $security
* #param ManagerRegistry $managerRegistry
* #param iterable $collectionExtensions
*/
public function __construct(
RolePermissionChecker $rolePermissionChecker,
Security $security,
ManagerRegistry $managerRegistry,
iterable $collectionExtensions = []
)
{
parent::__construct(
$managerRegistry,
$collectionExtensions
);
$this->rolePermissionChecker = $rolePermissionChecker;
$this->security = $security;
}
/**
* #param string $resourceClass
* #param string|null $operationName
* #param array $context
*
* #return bool
*/
public function supports(
string $resourceClass,
string $operationName = null,
array $context = []
): bool
{
return Offer::class === $resourceClass;
}
/**
* #inheritdoc
*/
public function getCollection(
string $resourceClass,
string $operationName = null,
array $context = []
): iterable
{
$collection = parent::getCollection(
$resourceClass,
$operationName,
$context
);
$user = $this->security->getUser();
if ($user instanceof User) {
$hasReadPermissionsOnOffer = fn($offer) => $this->rolePermissionChecker->hasReadPermission(
$offer,
$user
);
if ($collection instanceof Paginator) {
$newIteratorArray = [];
foreach ($collection->getIterator() as $offer) {
if ($hasReadPermissionsOnOffer($offer)) {
$newIteratorArray[] = $offer;
}
}
$paginatorReflectionProperty = new ReflectionProperty(
Paginator::class,
'paginator'
);
$paginatorReflectionProperty->setAccessible(true);
$paginator = $paginatorReflectionProperty->getValue($collection);
return new FilteredPaginator(
$paginator,
new ArrayIterator(
$newIteratorArray
),
$collection->getCurrentPage(),
$collection->getItemsPerPage(),
$collection->getLastPage(),
$collection->getTotalItems(),
);
} else if (
is_array($collection)
) {
$collection = array_filter(
$collection,
$hasReadPermissionsOnOffer,
);
}
}
return $collection;
}
}

How to pass form data to event subscriber on submit

I need to get the chosen brand for the MetaSubscriber getUrl callback. This function generates a URL like /brand-model based on the brand that gets passed to the function and the baseFieldName that's chosen in the metasubscriber (in this case being model).
This currently gives me the previous brand on updating and null on creating a new entry.
My question
Is there any way I can pass the submitted Brand to the getUrl function to generate my URLs?
My code
This is the MetaSubscriber in my Formbuilder, which calls the getUrl function to generate a URL for the added entry.
$builder->addEventSubscriber(
new AddMetaSubscriber(
'Glasses',
'GlassesDetail',
GlassesRepository::class,
'getUrl',
[
'getData.getId',
'getData.getBrand' //Need to get the submitted brand here
//I've tried with `brand` like it works for the model but I get an "Undefined method" error
],
'model'
)
);
Which gets the URL from the following function:
public function getUrl(string $url, int $id = null, string $brand = null): string
{
$query = $this
->createQueryBuilder('i')
->select('COUNT(i)')
->innerJoin('i.meta', 'm')
->where('m.url = :url')
->setParameter('url', $url);
if ($id !== null) {
$query
->andWhere('i.id != :id')
->setParameter('id', $id);
}
$url = strtolower($brand)."-".$url;
if ((int)$query->getQuery()->getSingleScalarResult() === 0) {
return $url;
}
return $this->getUrl(Model::addNumber($url), $id, $brand);
}
AddMetaSubscriber class
<?php
namespace Backend\Form\EventListener;
use Backend\Core\Engine\Model;
use Backend\Form\Type\MetaType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Use this class to add meta url generating to your symfony form.
*
* Example: $builder->addEventSubscriber(new AddMetaSubscriber(...));
*/
class AddMetaSubscriber implements EventSubscriberInterface
{
/**
* #var string - The URL shown in the backend will need this "action" to be generated.
*/
private $actionForUrl;
/**
* #var string - The field in the form where the URL should be generated for.
*/
private $baseFieldName;
/**
* #var string - Name of the class or the container service id, f.e.: 'moduleForUrl.repository.item',
*/
private $generateUrlCallbackClass;
/**
* #var string - Name of the public method which returns you the URL, f.e.: "getUrl"
*/
private $generateUrlCallbackMethod;
/**
* #var array - Extra parameters you want to include in the AJAX call to get the URL, f.e.: ["getData.getLocale", "getForm.getParent.getParent.getData.getMenuEntityId"]
*/
private $generateUrlCallbackParameterMethods;
/**
* #var string - The URL shown in the backend will need this "module" to be generated.
*/
private $moduleForUrl;
public function __construct(
string $moduleForUrl,
string $actionForUrl,
string $generateUrlCallbackClass,
string $generateUrlCallbackMethod,
array $generateUrlCallbackParameterMethods,
string $baseFieldName = 'title'
) {
$this->moduleForUrl = $moduleForUrl;
$this->actionForUrl = $actionForUrl;
$this->generateUrlCallbackClass = $generateUrlCallbackClass;
$this->generateUrlCallbackMethod = $generateUrlCallbackMethod;
$this->generateUrlCallbackParameterMethods = $generateUrlCallbackParameterMethods;
$this->baseFieldName = $baseFieldName;
}
public static function getSubscribedEvents(): array
{
// Tells the dispatcher that you want to listen on the form.pre_set_data
// event and that the preSetData method should be called.
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event)
{
$event->getForm()->add(
'meta',
MetaType::class,
[
'base_field_name' => $this->baseFieldName,
'detail_url' => Model::getUrlForBlock($this->moduleForUrl, $this->actionForUrl),
'generate_url_callback_class' => $this->generateUrlCallbackClass,
'generate_url_callback_method' => $this->generateUrlCallbackMethod,
'generate_url_callback_parameters' => $this->buildCallbackParameters($event),
]
);
}
private function buildCallbackParameters(FormEvent $event): array
{
$parameters = [];
foreach ($this->generateUrlCallbackParameterMethods as $generateUrlCallbackParameterMethod) {
$parameter = $event;
$methods = explode('.', $generateUrlCallbackParameterMethod);
foreach ($methods as $method) {
$parameter = $parameter->{$method}();
}
$parameters[] = $parameter;
}
return $parameters;
}
}

ezplatform extracting the uri of an image for the current language of the current site access

Is there a best practice to extract the uri of an image in the current translation of the current site access in twig?
We have an object with a translatable image field. Rendering the image with the helper: ez_render_field works fine.
But I now need to also extract the uri of the image for the current siteaccess but cannot find a way of doing this.
Trying to use the ez_field just results in
{{ ez_field(content, "image_1").uri }}
An exception has been thrown during the rendering of a template
("Catchable Fatal Error: Object of class
eZ\Publish\API\Repository\Values\Content\Field could not be converted
to string").
The content object looks like:
here is the standard way to achieve this.
where image is the field's name and teaser is the image variante you defined. by default you have original , small , large and ....
{% set imgAlias = ez_image_alias( content.getField( "image" ), content.versionInfo, 'teaser' ) %}
{{ dump(imgAlias.url) }}
( imgAlias.url is what you are looking for. )
here is the link to Documentation: https://doc.ez.no/display/DEVELOPER/ez_image_alias
I don't know if this is the best method, but this is what i came up with:
{{ getImageUri( content.fields.image, ezpublish.siteaccess ) }}
The custom twig functions
use Symfony\Component\Yaml\Yaml;
/**
* Class AppExtension
*
* #package AppBundle\Twig
*/
class AppExtension extends \Twig_Extension
{
public function getFilters()
{
return [
];
}
public function getFunctions()
{
return [
new \Twig_SimpleFunction('getYml', [$this, 'getYml']),
new \Twig_SimpleFunction('getImageUri', [$this, 'getImageUri']),
];
}
/**
* Pass an image object and return the original image uri in the current site access
*
* #param $imageField
* #param $siteAccess
*
* #return mixed
*/
public function getImageUri( $imageField, $siteAccess ){
$languages = $this->getYml('ezplatform', 'ezpublish:system:'.$siteAccess->name.':languages');
if($languages){
foreach($languages as $language){
if( array_key_exists( $language, $imageField ) ){
return $imageField[$language]->uri;
}
}
}
return $imageField[array_keys($imageField)[0]]->uri;
}
/**
* Internal cache of the yml files already fetched
*
* #var array
*/
private $yamlCache = [];
/**
* Return content from a app/config/*.yml file. Pass in a : separated path to fetch what you need. Empty returns the whole yml as an array
*
* #param $fileMinusExt
* #param bool $path
* #param string $delimiter
*
* #return bool|mixed
*/
public function getYml($fileMinusExt, $path = false, $delimiter = ':')
{
if (in_array($fileMinusExt, $this->yamlCache)) {
$value = $this->yamlCache[$fileMinusExt];
} else {
$value = Yaml::parse(file_get_contents('../app/config/' . $fileMinusExt . '.yml'));
}
if ($path === false) {
return $value;
}
return $this->_getYmlPath($value, explode($delimiter, $path));
}
/**
* Extract value from array
*
* #param $values
* #param $parts
*
* #return bool
*/
private function _getYmlPath($values, $parts)
{
try {
$subVal = $values[array_shift($parts)];
if (count($parts) > 0) {
return $this->_getYmlPath($subVal, $parts);
}
return $subVal;
} catch (\Exception $e) {
return false;
}
}
}

codeigniter is returning database row considered oop

im using Codeigniter 3.0 query builder, my question when ever my model return a user i'm returning a database row. not an object -its stdobject but not try object- is this anything related to oop practice ?
my auth model is simple
class user extend CI_MODEL{
funciton attempt($user,$pass){
//do validation and fetch user and compare pass etc...
$query = $this->db->get_where('users',$where);
return $query->result() //now this is my line of question
}
}
so i think this is nothing related to oop ? -or am i wrong ? - its just procedural code using classes for organization !.
so what is the correct way in oop manner ?
I have goan through many auth libraries for codeigntier to see how they do it, and all what i see is they save user row to an array variable in model. yet all users are still inside only 1 user object .
should i create an abstract class/interfaces for user object and pass database row to it every time i fetch a user before i save them to my Big ci_model ?
if so is this doable in codeigniter ? where would i put this abstract classes ?
I have done somthing like this, and yes I have created a model_row class to pass all data to in a array_walk style:
if ($qry = $this->db->get()) {
$res = $qry->result();
return $this->_instantiateRows($res);
}
Function _instantiateRows():
/**
* Get the row class name
* Checks if a class exists with the model name _Row
* #return string
*/
private function _getRowClass() {
$modelName = get_class($this);
return class_exists($modelName.'_Row') ? $modelName.'_Row' : 'Model_Row';
}
/**
* Formats results into model row classes
* #param array $results
* #return array
*/
protected function _instantiateRows($results) {
$rowClass = $this->_getRowClass();
$self = $this;
array_walk($results,function(&$row,$k) use ($rowClass, $self) {
$row = new $rowClass($row,$self,$k);
});
return $results;
}
Then a row class:
/**
* Model row class
* Acts as a baseclass and a fallback class for database rows
* Implements standard methods, for saving, getting the ID, and setting field
* values.
* #property $_model MY_Model
* #property $_key Original Array key
* #property $_ID_FIELD name of the id field
*/
class Model_Row
{
protected $_isNew = True;
protected $_model = False;
protected $_key = False;
protected $_ID_FIELD = 'id';
protected $_ID_ORIGINAL = False;
/**
* C'tor
* Sets up the object with data from the row
* #param object $data
* #param object $model
* #param int $key
* #param string $id_field
*/
public function __construct($data,$model,$key=False) {
$this->_key = $key;
// If no key is specified then it must be new / Not from database
if ($this->_key !== False)
$this->_isNew = False;
$data = (array)$data;
$this->_model = $model;
$this->_ID_FIELD = $model->idField;
$this->set($data);
// Ensure ID Field is set.
$idF = $this->_ID_FIELD;
if (!isset($this->$idF))
$this->$idF = null;
}
/**
* Get the ID field
* ID Field could be named differently for each model, this is an easy
* shortcut to it.
* #param string $setTo - set the id to this value
* #return string
*/
public function id($setTo=False) {
$idF = $this->_ID_FIELD;
if ($setTo !== False) {
if ($this->_ID_ORIGINAL === False && !$this->_isNew)
$this->_ID_ORIGINAL = $this->$idF;
$this->set($idF,$setTo);
}
return $this->$idF !== null ? (string)$this->$idF : False;
}
/**
* Save this row
* #return bool
*/
public function save() {
$wheres = array();
if (!$this->_isNew) {
$idF = $this->_ID_FIELD;
$wheres[$idF] = $this->_ID_ORIGINAL ?: $this->id();
}
$res = $this->_model->set($this,$wheres);
if ($this->id() === False)
$this->id($this->_model->insertID());
// Reset the original id field
$this->_ID_ORIGINAL = False;
$this->_isNew = False;
if ($res)
return $this;
return False;
}
/**
* Set object properties
* can be passed by array field => value
* #param mixed $field
* #param mixed $value
* #return null
*/
public function set($field,$value=False) {
if ((is_array($field) || is_object($field)) && $value === False) {
if (is_object($field))
$field = (array)$field;
foreach($field as $f => $v)
$this->set($f,$v);
}
else {
if (method_exists($this, 'set_'.$field))
call_user_func(array($this,'set_'.$field), $value);
else
$this->$field = $value;
}
}
}
The point of the _getRowClass is to check for a class called model_name_row if this exists, then instantiate the data to this class, otherwise fall back to the baseclass model_row
There are some other things your model will need too, because the row class will be passed the model, so your model will need a public $idField='id' , and then this function can be usefull on your model:
/**
* Create a new record using the model row class
* #param mixed $data
* #return Model_Row
*/
public function newRow($data=array()) {
$rowClass = $this->_getRowClass();
return new $rowClass($data,$this);
}
So you can do $newRow = $this->Model->newRow($data) which will create a new row, then can call $newRow->save() and other methods if set...
* EDIT
Also to point out, I use $this->_model->set($this, $wheres) on the row class, this is because I have defined a baseclass with a public setter:
/**
* Setter
* #param mixed $data object or array
* #param mixed $wheres object or array query
* #return bool
*/
public function set($data,$wheres=array()) {
if (!$this->checkTableSet())
return False;
if (empty($wheres)) {
return $this->db->insert($this->table,$data);
}
else {
$this->db->where($wheres);
return $this->db->update($this->table,$data);
}
}
$this->table is a model variable with the table name, e.g. protected $table='users'; and the function checkTableSet() simply checks whether this has been set and not empty..

Which is the best way to display 'flash messages' in kohana v3?

I would like to know the best way to display flash messages in Kohana v3?
Some tutorials or examples would be helpful.
Do you mean like Kohana 2.x's flash session variables?
The latest Kohana supports get_once() which is pretty similar to the old flash session variables.
$session = Session::instance();
$session->set('test', 'Hello, World!');
// The session variable is returned and removed.
$test = $session->get_once('test');
I think the get_once is a great function, but what if you want to keep the data actually separate from the regular data, here's a basic class that overloads "Session" so that you can use "codeigniter" style flashdata calls with any data-store.
<?php defined('SYSPATH') or die('No direct script access.');
abstract class Session extends Kohana_Session {
/**
* This calls the parent Kohana_Session constructor and processes
* new flashdata to flashdata, and flashdata to old flashdata
*
* #param array configuration
* #param string session id
* #return void
* #uses Kohana_Session::__construct
*/
public function __construct(array $config = NULL, $id = NULL)
{
parent::__construct($config,$id);
if(array_key_exists('___of',$this->_data)){
//Remove old Flash data
unset($this->_data['___of']);
}
if(array_key_exists('___flash',$this->_data)){
//Move current last requests flash data to old flash data
$this->_data['___of'] = $this->_data['___flash'];
unset($this->_data['___flash']);
}
if(array_key_exists('___nf',$this->_data)){
//Move Last Requests added data to the flash data
$this->_data['___flash'] = $this->_data['___nf'];
unset($this->_data['___nf']);
}
}
/**
* keeps a variable set in the sessions flashdata array.
*
* $session->set_flashdata('foo', 'bar');
*
* #param string variable name
* #param ...
* #return $this
*/
public function keep_flashdata($k)
{
$args = func_get_args();
if(array_key_exists('___of',$this->_data)){
foreach($args as $key){
if(array_key_exists($key,$this->_data['___of'])){
//So we were going to trash it...
$this->set_flashdata($k,$this->_data['___of'][$key],true);
}
}
}
$this->_data['___nf'][$key] = $value;
return $this;
}
/**
* Set a variable in the sessions flashdata array.
*
* $session->set_flashdata('foo', 'bar');
*
* #param string variable name
* #param mixed value
* #return $this
*/
public function set_flashdata($key, $value, $current=false)
{
if(!array_key_exists('___nf',$this->_data)){
$this->_data['___nf'] = array();
}
$this->_data['___nf'][$key] = $value;
if($current){
if(!array_key_exists('___flash',$this->_data)){
$this->_data['___flash'] = array();
}
$this->_data['flash'][$key] = $value;
}
return $this;
}
/**
* Set a variable by reference in the sessions flashdata array.
*
* $session->bind_flashdata('foo', $foo);
*
* #param string variable name
* #param mixed referenced value
* #return $this
*/
public function bind_flashdata($key, & $value)
{
if(!array_key_exists('___nf',$this->_data)){
$this->_data['___nf'] = array();
}
$this->_data['___nf'][$key] =& $value;
return $this;
}
/**
* Removes a variable in the session array.
*
* $session->delete_flashdata('foo');
*
* #param string variable name
* #param ...
* #return $this
*/
public function delete_flashdata($key)
{
$args = func_get_args();
if(array_key_exists('___nf',$this->_data)){
foreach ($args as $key)
{
if(array_key_exists($key,$this->_data['___nf'])){
unset($this->_data['___nf'][$key]);
}
}
}
return $this;
}
/**
* Get a variable from the sessions flashdata array.
*
* $foo = $session->get_flashdata('foo');
*
* #param string variable name
* #param mixed default value to return
* #return mixed
*/
public function get_flashdata($key, $default = NULL)
{
if(array_key_exists('___flash',$this->_data) && array_key_exists($key,$this->_data['___flash'])){
return $this->_data['___flash'][$key];
} else if(array_key_exists('___nf',$this->_data) && array_key_exists($key,$this->_data['___nf'])){
return $this->_data['___nf'][$key];
}
return $default;
}
/**
* Get and delete a variable from the session array.
*
* $bar = $session->get_once('bar');
*
* #param string variable name
* #param mixed default value to return
* #return mixed
*/
public function get_flashdata_once($key, $default = NULL)
{
$value = $this->get_flashdata($key, $default);
if(array_key_exists($key, $this->_data['___flash'])){
unset($this->_data['___flash'][$key]);
}
if(array_key_exists($key, $this->_data['___nf'])){
unset($this->_data['___nf'][$key]);
}
return $value;
}
}
?>
I realize there was an answer to this, and like i stated before, the get_once method is great and all, but i enjoy auto garbage collection much more.
If you have any improvements on this code, let me know, its been great to me so far.
Have a look at this module, it might be what you are looking for https://github.com/daveWid/message
I've written a really simple class for this once. Check it out below. Usage examples below
class Notice {
private static $session;
private static $initialized = false;
// current notices
private static $notices = array();
function __construct() {
}
static function init() {
self::$session = Session::instance();
self::$notices['current'] = json_decode(self::$session->get_once('flash'));
if(!is_array(self::$notices['current'])) self::$notices['current'] = array();
self::$initialized = true;
}
static function add($notice, $key=null) {
if(!self::$initialized) self::init();
if(!is_null($key)) {
self::$notices['new'][$key] = $notice;
} else {
self::$notices['new'][] = $notice;
}
self::$session->set('flash', json_encode(self::$notices['new']));
return true;
}
static function get($item = null) {
if(!self::$initialized) self::init();
if($item == null) {
return self::$notices['current'];
}
if(!array_key_exists($item, self::$notices['current']))
return null;
return self::$notices['current'][$item];
}
}
Examples (provided this class is saved as APPPATH . 'classes/notice.php'):
Notice::add('Something great has happened!');
Notice::add('Exciting! I\'ve got something to tell you!', 'message');
echo Notice::get('message'); // "Exciting! I've got ..."
foreach(Notice::get() as $message) {
echo $i++ . $message .'<br />';
}
EDIT: funny... for some reason this question popped up somewhere, didn't notice it was a really old one... sorry for that!
I am using https://github.com/synapsestudios/kohana-notices in my project and I am very happy with it.

Categories