Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

### Changed

- Changed `Phalcon\Forms\Element\Select::render()` to use `TagFactory`-based `Html\Helper\Input\Select` instead of the deprecated `Phalcon\Tag\Select` [#16894](https://github.com/phalcon/cphalcon/issues/16894)
- Changed `Phalcon\Forms\Element\AbstractElement::getLocalTagFactory()` to throw `Phalcon\Forms\Exception` instead of silently creating a new `TagFactory` when neither `setTagFactory()` nor a parent `Form` provides one [#16894](https://github.com/phalcon/cphalcon/issues/16894)
- Changed `Phalcon\Assets\Manager` filter type check from `is_object()` to `typeof` and updated the error message to `"The filter is not valid"` [#16889](https://github.com/phalcon/cphalcon/issues/16889)
- Changed `Phalcon\Cache\AbstractCache::doDeleteMultiple()` to delegate to the storage adapter's `deleteMultiple()` instead of looping over individual `delete()` calls [#16859](https://github.com/phalcon/cphalcon/issues/16859)
- Changed calls to `globals_get` and `globals_set` in the code with `Phalcon\Support\Settings::get()/set()` [#16884](https://github.com/phalcon/cphalcon/issues/16884)
Expand All @@ -15,6 +17,9 @@

### Added

- Added `Phalcon\Html\Helper\Input\Select\SelectDataInterface`, `Phalcon\Html\Helper\Input\Select\ArrayData`, and `Phalcon\Html\Helper\Input\Select\ResultsetData` as data providers for the `Select` helper [#16894](https://github.com/phalcon/cphalcon/issues/16894)
- Added `Phalcon\Html\Helper\Input\Select::fromData()` to populate select options from a `SelectDataInterface` provider, with optgroup support [#16894](https://github.com/phalcon/cphalcon/issues/16894)
- Added named static factory methods `Phalcon\Forms\Exception::tagFactoryNotFound()` and `Phalcon\Forms\Exception::usingParameterRequired()` [#16894](https://github.com/phalcon/cphalcon/issues/16894)
- Added `deleteMultiple()` to `Phalcon\Storage\Adapter\*` to delete multiple keys in a single operation using native batch capabilities per adapter [#16859](https://github.com/phalcon/cphalcon/issues/16859)
- Added key validation per entry in `Phalcon\Cache\AbstractCache::doDeleteMultiple()` throwing `InvalidArgumentException` for keys containing invalid characters [#16859](https://github.com/phalcon/cphalcon/issues/16859)
- Added `Phalcon\Html\Helper\FriendlyTitle` - available via `TagFactory` as `friendlyTitle` [#16892(https://github.com/phalcon/cphalcon/issues/16892)
Expand Down
22 changes: 5 additions & 17 deletions phalcon/Forms/Element/AbstractElement.zep
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ use InvalidArgumentException;
use Phalcon\Di\DiInterface;
use Phalcon\Di\Di;
use Phalcon\Filter\Validation\ValidatorInterface;
use Phalcon\Forms\Form;
use Phalcon\Forms\Exception;
use Phalcon\Html\Escaper;
use Phalcon\Forms\Form;
use Phalcon\Html\TagFactory;
use Phalcon\Messages\MessageInterface;
use Phalcon\Messages\Messages;
Expand Down Expand Up @@ -524,7 +523,7 @@ abstract class AbstractElement implements ElementInterface
*/
protected function getLocalTagFactory() -> <TagFactory>
{
var container, escaper, tagFactory;
var container, tagFactory;

let tagFactory = null;

Expand All @@ -542,24 +541,13 @@ abstract class AbstractElement implements ElementInterface
if tagFactory === null {
let container = Di::getDefault();

if likely true === container->has("tag") {
if container !== null && true === container->has("tag") {
let tagFactory = container->getShared("tag");
}
}

/**
* All failed, create a new TagFactory
*/
if tagFactory === null {
let container = Di::getDefault();

if likely true === container->has("escaper") {
let escaper = container->getShared("escaper");
} else {
let escaper = new Escaper();
}

let tagFactory = new TagFactory(escaper);
if unlikely tagFactory === null {
throw Exception::tagFactoryNotFound();
}

let this->tagFactory = tagFactory;
Expand Down
102 changes: 92 additions & 10 deletions phalcon/Forms/Element/Select.zep
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

namespace Phalcon\Forms\Element;

use Phalcon\Tag\Select as SelectTag;
use Phalcon\Forms\Exception;
use Phalcon\Html\Helper\Input\Select\ArrayData;
use Phalcon\Html\Helper\Input\Select\ResultsetData;
use Phalcon\Html\TagFactory;
use Phalcon\Mvc\Model\ResultsetInterface;

/**
* Component SELECT (choice) for forms
Expand All @@ -25,8 +29,9 @@ class Select extends AbstractElement
/**
* Constructor
*
* @param object|array options
* @param array attributes
* @param string name
* @param object|array|null options
* @param array attributes
*/
public function __construct(string name, options = null, array attributes = [])
{
Expand Down Expand Up @@ -70,13 +75,90 @@ class Select extends AbstractElement
*/
public function render(array attributes = []) -> string
{
/**
* Merged passed attributes with previously defined ones
*/
return SelectTag::selectField(
this->prepareAttributes(attributes),
this->optionsValues
);
var attrs, emptyText, emptyValue, html, name, options,
select, tagFactory, using, useEmpty, value;

let attrs = this->prepareAttributes(attributes);

let name = attrs[0];
unset attrs[0];

if isset attrs["value"] {
let value = attrs["value"];
unset attrs["value"];
} else {
let value = null;
}

if isset attrs["useEmpty"] {
let useEmpty = attrs["useEmpty"];
} else {
let useEmpty = false;
}

if isset attrs["emptyValue"] {
let emptyValue = attrs["emptyValue"];
} else {
let emptyValue = "";
}

if isset attrs["emptyText"] {
let emptyText = attrs["emptyText"];
} else {
let emptyText = "Choose...";
}

if isset attrs["using"] {
let using = attrs["using"];
} else {
let using = null;
}

unset attrs["useEmpty"];
unset attrs["emptyValue"];
unset attrs["emptyText"];
unset attrs["using"];

if !isset attrs["name"] {
let attrs["name"] = name;
}

if !strpos(name, "[") && !isset attrs["id"] {
let attrs["id"] = name;
}

let tagFactory = this->getLocalTagFactory(),
select = tagFactory->newInstance("inputSelect");

select->__invoke("", "", attrs);

if value !== null {
select->selected((string) value);
}

if useEmpty {
select->addPlaceholder(emptyText, emptyValue, [], true);
}

let options = this->optionsValues;

if typeof options == "array" {
select->fromData(new ArrayData(options));
} elseif typeof options == "object" && options instanceof ResultsetInterface {
if unlikely using === null || typeof using != "array" {
throw Exception::usingParameterRequired();
}

select->fromData(new ResultsetData(options, using));
}

let html = (string) select;

if html === "" {
return tagFactory->newInstance("element")->__invoke("select", PHP_EOL, attrs, true);
}

return html;
}

/**
Expand Down
12 changes: 12 additions & 0 deletions phalcon/Forms/Exception.zep
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,17 @@ namespace Phalcon\Forms;
*/
class Exception extends \Exception
{
public static function tagFactoryNotFound() -> <Exception>
{
return new self(
"A TagFactory must be provided via setTagFactory() or through a parent Form"
);
}

public static function usingParameterRequired() -> <Exception>
{
return new self(
"The 'using' parameter is required for resultset options"
);
}
}
32 changes: 32 additions & 0 deletions phalcon/Html/Helper/Input/Select.zep
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Phalcon\Html\Helper\Input;

use Phalcon\Html\Helper\AbstractList;
use Phalcon\Html\Helper\Input\Select\SelectDataInterface;

/**
* Class Select
Expand Down Expand Up @@ -98,6 +99,37 @@ class Select extends AbstractList
return this;
}

/**
* Populates the select from a data provider.
*
* Flat entries: key = option value, value = label string.
* Optgroup entries: key = group label, value = [value => label] array.
*
* @param SelectDataInterface data
*
* @return Select
*/
public function fromData(<SelectDataInterface> data) -> <Select>
{
var key, subKey, subValue, value;

for key, value in data->getOptions() {
if typeof value == "array" {
this->optGroup((string) key);

for subKey, subValue in value {
this->add((string) subValue, (string) subKey);
}

this->optGroup((string) key);
} else {
this->add((string) value, (string) key);
}
}

return this;
}

/**
* Creates an option group
*
Expand Down
41 changes: 41 additions & 0 deletions phalcon/Html/Helper/Input/Select/ArrayData.zep
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

/**
* This file is part of the Phalcon Framework.
*
* (c) Phalcon Team <team@phalcon.io>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

namespace Phalcon\Html\Helper\Input\Select;

/**
* Wraps a plain PHP array as a SELECT data provider.
*
* Keys are option values; string values are labels;
* array values define optgroups.
*/
class ArrayData implements SelectDataInterface
{
/**
* @var array
*/
protected data = [];

/**
* @param array data
*/
public function __construct(array data = [])
{
let this->data = data;
}

/**
* @return array
*/
public function getOptions() -> array
{
return this->data;
}
}
80 changes: 80 additions & 0 deletions phalcon/Html/Helper/Input/Select/ResultsetData.zep
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

/**
* This file is part of the Phalcon Framework.
*
* (c) Phalcon Team <team@phalcon.io>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

namespace Phalcon\Html\Helper\Input\Select;

use InvalidArgumentException;
use Phalcon\Mvc\Model\ResultsetInterface;

class ResultsetData implements SelectDataInterface
{
/**
* @var ResultsetInterface
*/
protected resultset;

/**
* @var array
*/
protected using = [];

/**
* @param ResultsetInterface resultset
* @param array using
*/
public function __construct(<ResultsetInterface> resultset, array using)
{
if unlikely count(using) !== 2 {
throw new InvalidArgumentException(
"The 'using' parameter requires exactly two values"
);
}

let this->resultset = resultset;
let this->using = using;
}

/**
* @return array
*/
public function getOptions() -> array
{
var option, optionText, optionValue, options, usingZero, usingOne;

let usingZero = this->using[0],
usingOne = this->using[1],
options = [];

for option in this->resultset {
if typeof option == "object" {
if method_exists(option, "readAttribute") {
let optionValue = option->readAttribute(usingZero),
optionText = option->readAttribute(usingOne);
} else {
let optionValue = option->{usingZero},
optionText = option->{usingOne};
}
} else {
if unlikely typeof option != "array" {
throw new InvalidArgumentException(
"Resultset returned an invalid value"
);
}

let optionValue = option[usingZero],
optionText = option[usingOne];
}

let options[optionValue] = optionText;
}

return options;
}
}
22 changes: 22 additions & 0 deletions phalcon/Html/Helper/Input/Select/SelectDataInterface.zep
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

/**
* This file is part of the Phalcon Framework.
*
* (c) Phalcon Team <team@phalcon.io>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

namespace Phalcon\Html\Helper\Input\Select;

/**
* Interface for SELECT option data providers.
*
* Return format: [value => label] for flat options;
* [groupLabel => [value => label, ...]] for optgroups.
*/
interface SelectDataInterface
{
public function getOptions() -> array;
}
Loading
Loading