Comments (24)
+5
from phalcon.
+1
And please, selected
attr too :)
from phalcon.
@scrnjakovic Most likely no. We will continue bug fixes and release some minor releases for 3.x branch in the future, but we are now going to concentrate fully on version 4.0.
from phalcon.
+1
from phalcon.
+1
from phalcon.
The only think I'm not liking is the "class" option you have listed. The second parameter is kinda arbitrary really. It would be less gimmicky if the options parser were written to, for each key, take either a string, array, or closure($data):
$this->tag->select([
'robotId',
Robots::find(),
'using' => ['id', 'name'],
'options' => [
'class' => function($robot){return ($robot->id != 2) ? 'favorite' : false;},
'disabled' => function($robot){return $robot->is_destroyed ? 'disabled' : false;},
'dir' => 'rtl',
],
]);
It is still a touch confusing to read the code as it's not obvious if 'options' refers to the select tag, or its options' tags. Thoughts?
from phalcon.
+1
from phalcon.
Hey, i quickly created a custom element to handle this, here is my code:
<?php
use Phalcon\Forms\Element;
class SelectEnhanced extends Element {
protected $optionsElement = array();
public function __construct($elementName, $elementValues = NULL, $parameters = NULL) {
$this->setName($elementName);
if($parameters['useEmpty']) {
if(!isset($parameters['emptyText'])) {
$parameters['emptyText'] = 'Choose...';
}
$this->addOption(array('value' => '', 'content' => $parameters['emptyText']));
}
if(!empty($parameters['options'])){
$haveOptions = true;
}
else {
$haveOptions = false;
}
// If we got data to generate optionElements
if(!empty($elementValues)) {
foreach($elementValues as $elementValue) {
// Initialize optionElement parameters
$optionParameters = array('value' => $elementValue->getId(), 'content' => $elementValue->getName());
// If somes options are defined to set attributes on optionElements
if($haveOptions) {
foreach($parameters['options'] as $attribute => $condition) {
// If condition is string assign it to result
if(is_string($condition)) {
$result = $condition;
}
// Otherwise execute condition to get result
else {
$result = $condition($elementValue);
}
// if condition is passed we had attribute with his value
if($result) {
$optionParameters[$attribute] = $result;
}
}
}
// add optionElement with parameters
$this->addOption($optionParameters);
}
}
}
public function render($attributes = null) {
// Set new attributes passed in parameter
$attributes = $this->prepareAttributes($attributes);
if(!empty($attributes)) {
foreach($attributes as $attrName => $attrValue) {
$this->setAttribute($attrName, $attrValue);
}
}
$html = '<select name="'.$this->getName().'"';
// write every attributes of select element in DOM
$attributes = $this->getAttributes();
if(!empty($attributes)) {
foreach($attributes as $attrName => $attrValue) {
$html .= ' '.$attrName.'="'.$attrValue.'"';
}
}
$html .= '>';
if(!empty($this->optionsElement)) {
foreach($this->optionsElement as $optionElement) {
// extract value an content from optionElement
$value = $optionElement['value'];
unset($optionElement['value']);
$content = $optionElement['content'];
unset($optionElement['content']);
$html .= '<option value="'.$value.'"';
// write every attributes of optionElement in DOM
if(!empty($optionElement)) {
foreach($optionElement as $attrName => $attrValue) {
$html .= ' '.$attrName.'="'.$attrValue.'"';
}
}
$html .= '>';
$html .= $content.'</option>';
}
}
$html .= '</select>';
return $html;
}
public function addOption($parameters) {
array_push($this->optionsElement, $parameters);
}
}
It's not perfect and could be improved with handling 'using' => ['id', 'name']
(actually i forced it to use $myObject->getId()
and $myObject->getName()
)
Here is an use case example in my Form class :
// Category
$categorys = $this->modelsManager->createBuilder()
->from('Category')
->where('type = :type:', array('type' => \Category::TYPE_SERVICE))
->andWhere('enabled = true')
->orderBy('lft')
->getQuery()
->execute();
$element = new \SelectEnhanced('category', $categorys,
array(
'useEmpty' => true,
'emptyText' => '',
'options' => array(
'disabled' => function($category){return !$category->getParentId() ? 'disabled' : false;},
'class' => 'myCustomClass'
)
)
);
$element->setLabel('Category');
$element->setAttribute('required', '');
$this->add($element);
Let me know if you like it and/or have suggestions to improve it !
from phalcon.
+1
from phalcon.
+1
from phalcon.
+1
from phalcon.
+1
from phalcon.
+1
from phalcon.
+1
from phalcon.
I've made these from 2.0 branch
- I had to completely port Tag\Select since _optionsFromResultSet() and _optionsFromArray() are declared private.
- I extendend Forms\Element\Select so it uses newly created Tag\Select to render element.
- I added ::_renderAttributes() to Tag\Select since in 1.3 Phalcon\Tag doesn't have that method (I ported from 2.0).
How does it work ?
See using
option? First two elements in array have same meaning as before, but each element after the first two will be used to generate option attributes.
use Jungle\Backend\Forms\Element\Select as SelectEnchanced;
$categories = new SelectEnchanced('categories');
$categories->setLabel($this->translator->_('LABEL_CATEGORIES'));
$categories->setOptions(TourCategoryComplex::find());
$categories->setAttributes(array(
'class' => 'form-control',
'multiple' => true,
'name' => 'categories[]',
'using' => array('id', 'full_name', 'parent_id', 'created_at')
));
$this->add($categories);
This will generate (Category with id 1 is not visible since it's root that holds first level categories. Just so you dont get confused):
<select id="categories" name="categories[]" class="form-control" multiple="1">
<option value="4" parent_id="1" created_at="2015-18-04 01:39:14">Test cat 3</option>
<option value="2" parent_id="1" created_at="2015-18-04 01:36:35">Test cat 1</option>
<option value="3" parent_id="2" created_at="2015-18-04 01:37:24">Test cat 1 > Test cat 2</option>
</select>
Files
NOTE THE NAMESPACES. CHANGE THEM SO THEY FIT YOU
Forms\Element\Select
Only difference here is that we're rendering field with our Tag\Select.
<?php
namespace Jungle\Backend\Forms\Element;
use Phalcon\Forms\Element\Select as PhSelect;
/**
* Phalcon\Forms\Element\Select
*
* Component SELECT (choice) for forms
*/
class Select extends PhSelect {
/**
* Renders the element widget returning html
*
* @param array attributes
* @return string
*/
public function render($attributes = null)
{
/**
* Merged passed attributes with previously defined ones
*/
return \Jungle\Tag\Select::selectField($this->prepareAttributes($attributes), $this->_optionsValues);
}
}
Tag\Select
This is Phalcon\Tag\Select from branch 2.0 rewritten in PHP with ::_renderAttributes() added to it as originally Phalcon\Tag from 2.0 branch has that method and Phalcon\Tag\Select utilizes it, but it's missing in Phalcon\Tag from 1.3, so instead of rewriting Phalcon\Tag as well, I just added that method to Tag\Select.
NOTE: It's late and I haven't ported _optionsFromArray() from Zephir to PHP, so currently it only works with objects
<?php
namespace Jungle\Tag;
use Phalcon\Tag\Exception;
use Phalcon\Tag as BaseTag;
/**
* Jungle\Tag\Select
*
* Generates a SELECT html tag using a static array of values or a Phalcon\Mvc\Model resultset
*/
abstract class Select
{
/**
* Generates a SELECT tag
*
* @param array parameters
* @param array data
*/
public static function selectField($parameters, $data = null)
{
if (!is_array($parameters)) {
$params = [$parameters, $data];
} else {
$params = $parameters;
}
if (isset($params[0])) {
$id = $params[0];
} else {
$params[0] = $params['id'];
}
/**
* Automatically assign the id if the name is not an array
*/
if (strpos($id, '[') === false) {
if (!isset($params['id'])) {
$params['id'] = $id;
}
}
if (isset($params['name'])) {
$name = $params['name'];
} else {
$params['name'] = $id;
}
if (!isset($params['value'])) {
$value = BaseTag::getValue($id, $params);
} else {
$value = $params['value'];
unset($params['value']);
}
if (isset($params['useEmpty'])) {
$useEmpty = $params['useEmpty'];
if (isset($params['emptyValue'])) {
$emptyValue = $params['emptyValue'];
unset($params['emptyValue']);
} else {
$emptyValue = '';
}
if (isset($params['emptyText'])) {
$emptyText = $params['emptyText'];
unset($params['emptyText']);
} else {
$emptyText = 'Choose...';
}
unset($params['useEmpty']);
}
if (isset($params[1])) {
$options = $params[1];
} else {
$options = $data;
}
if (is_object($options)) {
/**
* The options is a resultset
*/
if (isset($params['using'])) {
$using = $params['using'];
if (!is_array($using) && !is_object($using)) {
throw new Exception("The 'using' parameter should be an array");
}
} else {
throw new Exception("The 'using' parameter is required");
}
}
unset($params['using']);
$code = self::_renderAttributes("<select", $params) . ">" . PHP_EOL;
if ($useEmpty) {
/**
* Create an empty value
*/
$code .= "\t<option value=\"" . $emptyValue . "\">" . $emptyText . "</option>" . PHP_EOL;
}
if (is_object($options)) {
/**
* Create the SELECT's option from a resultset
*/
$code .= self::_optionsFromResultset($options, $using, $value, "</option>" . PHP_EOL);
} else {
if (is_array($options)) {
/**
* Create the SELECT's option from an array
*/
$code .= self::_optionsFromArray($options, $value, "</option>" . PHP_EOL);
} else {
throw new Exception("Invalid data provided to SELECT helper");
}
}
$code .= "</select>";
return $code;
}
/**
* Generate the OPTION tags based on a resulset
*
* @param Phalcon\Mvc\Model\Resultset resultset
* @param array using
* @param mixed value
* @param string closeOption
*/
protected static function _optionsFromResultset($resultset, $using, $value, $closeOption)
{
$code = "";
$params = null;
if (is_array($using)) {
$usingZero = $using[0];
$usingOne = $using[1];
# Unseting 'using' options which are used for value and text
unset($using[0], $using[1]);
}
foreach ($resultset as $option) {
if (is_array($using)) {
if (is_object($option)) {
if (method_exists($option, "readAttribute")) {
$optionValue = $option->readAttribute($usingZero);
$optionText = $option->readAttribute($usingOne);
} else {
$optionValue = $option->$usingZero;
$optionText = $option->$usingOne;
}
# We're getting attributes from the object
$optionAttributes = array();
foreach ($using as $property) {
if (method_exists($option, "readAttribute")) {
$optionAttributes[$property] = $option->readAttribute($property);
} else {
$optionAttributes[$property] = $option->$poperty;
}
}
} else {
if (is_array($option)) {
$optionValue = $option[$usingZero];
$optionText = $option[$usingOne];
# Or from array?
$optionAttributes = array();
foreach ($using as $property) {
$optionAttributes[$property] = $option[$property];
}
} else {
throw new Exception("Resultset returned an invalid value");
}
}
# Now let's render them :)
$attributes = '';
foreach ($optionAttributes as $attribute => $value) {
$attributes = ' ' . $attribute . '="' . $value . '"';
}
/**
* If the value is equal to the option"s value we mark it as selected
*/
if (is_array($value)) {
if (in_array($optionValue, $value)) {
$code .= "\t<option selected=\"selected\" value=\"" . $optionValue . "\"" . $attributes . ">" . $optionText . $closeOption;
} else {
$code .= "\t<option value=\"" . $optionValue . "\"" . $attributes . ">" . $optionText . $closeOption;
}
} else {
if ($optionValue == $value) {
$code .= "\t<option selected=\"selected\" value=\"" . $optionValue . "\"" . $attributes . ">" . $optionText . $closeOption;
} else {
$code .= "\t<option value=\"" . $optionValue . "\"" . $attributes . ">" . $optionText . $closeOption;
}
}
} else {
/**
* Check if using is a closure
*/
if (is_object($using)) {
if (is_null($params)) {
$params = [];
}
$params[0] = $option;
$code .= call_user_func_array($using, $params);
}
}
}
return $code;
}
/**
* Generate the OPTION tags based on an array
*
* @param array data
* @param mixed value
* @param string closeOption
*/
protected static function _optionsFromArray($data, $value, $closeOption)
{
/*var code, optionValue, optionText, escaped;
let code = "";
for optionValue, optionText in data {
let escaped = htmlspecialchars(optionValue);
if typeof optionText == "array" {
let code .= "\t<optgroup label=\"" . escaped . "\">" . PHP_EOL . self::_optionsFromArray(optionText, value, closeOption) . "\t</optgroup>" . PHP_EOL;
} else {
if typeof value == "array" {
if in_array(optionValue, value) {
let code .= "\t<option selected=\"selected\" value=\"" . escaped . "\">" . optionText . closeOption;
} else {
let code .= "\t<option value=\"" . escaped . "\">" . optionText . closeOption;
}
} else {
if optionValue == value {
let code .= "\t<option selected=\"selected\" value=\"" . escaped . "\">" . optionText . closeOption;
} else {
let code .= "\t<option value=\"" . escaped . "\">" . optionText . closeOption;
}
}
}
}
return code;*/
}
/**
* Renders parameters keeping order in their HTML attributes
*/
protected static function _renderAttributes($code, $attributes)
{
//var order, escaper, attrs, attribute, value, escaped, key, newCode;
$order = [
"rel" => null,
"type" => null,
"for" => null,
"src" => null,
"href" => null,
"action" => null,
"id" => null,
"name" => null,
"value" => null,
"class" => null
];
$attrs = [];
foreach ($order as $key => $value) {
if (isset($attributes[$key])) {
$attribute = $attributes[$key];
$attrs[$key] = $attribute;
}
}
foreach ($attributes as $key => $value) {
if (!isset($attrs[$key])) {
$attrs[$key] = $value;
}
}
$escaper = BaseTag::getEscaperService();
unset($attrs["escape"]);
$newCode = $code;
foreach ($attrs as $key => $value) {
if (is_string($key) && !is_null($value)) {
if (is_array($value) || is_resource($value)) {
throw new Exception("Value at index: '" . $key . "' type: '" . gettype($value) . "' cannot be rendered");
}
if ($escaper) {
$escaped = $escaper->escapeHtmlAttr($value);
} else {
$escaped = $value;
}
$newCode .= " " . $key . "=\"" . $escaped . "\"";
}
}
return $newCode;
}
}
Hope someone will find this helpful. Use it on your own risk 😜
from phalcon.
change this line
$attributes = ' ' . $attribute . '="' . $value . '"';
to this
$attributes .= ' ' . $attribute . '="' . $value . '"';
from phalcon.
+1
from phalcon.
+1
from phalcon.
+1
from phalcon.
+1
from phalcon.
@sergeyklay is this fixed?
from phalcon.
$field = new Select("name", Model::find(), [
"using" => function($every_result_from_model_select /*record*/) {
return "<option value='1'>Option 1</option>"
}
]
keep in mind that only works with model::find, not with an array
from phalcon.
@sergeyklay any chances this will be implemented before 4.0? Need any help?
from phalcon.
Closing in favor of phalcon/cphalcon#13855. Will revisit if the community votes for it, or in later versions.
from phalcon.
Related Issues (20)
- [NFR]: Replace regexp in the router with arrays for extra performance
- Can't access page, 404 FORBIDDEN
- Can I use `$this->getRelated` on already cached data?
- [BUG]: Scrutinizer Fixes HOT 1
- [NFR]: ADD sticky for read write connection
- [NFR]: removeBehavior method in Model
- [NFR]:returnedValue not updated after dispatcher->forward
- [NFR]: No way to add html attributes to Select Tag. documentation inadequate HOT 1
- [BUG]: The "setDefault" method does not work for forms of type "text" with name "value" HOT 5
- update src folder links
- How to use cookie in phalcon 4.x ? HOT 1
- update tests folder links
- [NFR]: Complete rework of ORM HOT 3
- [BUG]: \Phalcon\Encryption\Crypt + named parameters/arguments throw fatal error. HOT 1
- [NFR]: Refactor Phalcon\Mvc\Router/Route HOT 1
- [NFR]: Refactor Phalcon\Mvc\Model\MetaData
- phalcon 3,4 webhook problem HOT 2
- [NFR]: What is the purpose of this library? HOT 3
- [NFR]: Add a getResult() or fetchAll() method to Resultset\Simple
- [BUG]: when i use JWT Builder , api returns blank HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from phalcon.