Coder Social home page Coder Social logo

Comments (24)

phresnel avatar phresnel commented on May 31, 2024 1

+5

from phalcon.

sergeyklay avatar sergeyklay commented on May 31, 2024 1

+1

And please, selected attr too :)

from phalcon.

sergeyklay avatar sergeyklay commented on May 31, 2024 1

 @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.

gianks avatar gianks commented on May 31, 2024

+1

from phalcon.

chiefGui avatar chiefGui commented on May 31, 2024

+1

from phalcon.

moderndeveloperllc avatar moderndeveloperllc commented on May 31, 2024

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.

xavier-rodet avatar xavier-rodet commented on May 31, 2024

+1

from phalcon.

xavier-rodet avatar xavier-rodet commented on May 31, 2024

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.

temuri416 avatar temuri416 commented on May 31, 2024

+1

from phalcon.

florentinarghira avatar florentinarghira commented on May 31, 2024

+1

from phalcon.

KorsaR-ZN avatar KorsaR-ZN commented on May 31, 2024

+1

from phalcon.

sersh88 avatar sersh88 commented on May 31, 2024

+1

from phalcon.

mzf avatar mzf commented on May 31, 2024

+1

from phalcon.

jimmycdinata avatar jimmycdinata commented on May 31, 2024

+1

from phalcon.

scrnjakovic avatar scrnjakovic commented on May 31, 2024

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.

EroNiC avatar EroNiC commented on May 31, 2024

change this line
$attributes = ' ' . $attribute . '="' . $value . '"';
to this
$attributes .= ' ' . $attribute . '="' . $value . '"';

from phalcon.

ndabAP avatar ndabAP commented on May 31, 2024

+1

from phalcon.

Izopi4a avatar Izopi4a commented on May 31, 2024

+1

from phalcon.

baychae avatar baychae commented on May 31, 2024

+1

from phalcon.

Slyvampy avatar Slyvampy commented on May 31, 2024

+1

from phalcon.

Mouvedia avatar Mouvedia commented on May 31, 2024

@sergeyklay is this fixed?

from phalcon.

Izopi4a avatar Izopi4a commented on May 31, 2024
$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.

scrnjakovic avatar scrnjakovic commented on May 31, 2024

@sergeyklay any chances this will be implemented before 4.0? Need any help?

from phalcon.

niden avatar niden commented on May 31, 2024

Closing in favor of phalcon/cphalcon#13855. Will revisit if the community votes for it, or in later versions.

from phalcon.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.