Skip to content
This repository has been archived by the owner on Jul 28, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from wavevision/feature/valid-props-object
Browse files Browse the repository at this point in the history
Feature/valid props object
  • Loading branch information
rozsival authored Nov 28, 2019
2 parents 0e4588b + a9cfc42 commit 05d2876
Show file tree
Hide file tree
Showing 17 changed files with 263 additions and 38 deletions.
7 changes: 2 additions & 5 deletions src/PropsControl/BaseControl.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Nette\Application\UI\Control;
use Nette\Application\UI\ITemplate;
use Nette\Bridges\ApplicationLatte\Template;
use Nette\InvalidStateException;

/**
* @property-read Template $template
Expand Down Expand Up @@ -42,10 +41,8 @@ final protected function getTemplateFile(?string $template = null): string
if (!$template) {
$template = static::DEFAULT_TEMPLATE;
}
$file = $this->getReflection()->getFileName();
if ($file === false) {
throw new InvalidStateException(sprintf('Unable to get filename for "%s".', static::class));
}
$file = (string)$this->getReflection()->getFileName();
return dirname($file) . "/templates/$template.latte";
}

}
1 change: 1 addition & 0 deletions src/PropsControl/ClassName.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,5 @@ function (?string $modifier): bool {
}
);
}

}
10 changes: 10 additions & 0 deletions src/PropsControl/Exceptions/InvalidProps.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare (strict_types = 1);

namespace Wavevision\PropsControl\Exceptions;

use Exception;

class InvalidProps extends Exception
{

}
10 changes: 10 additions & 0 deletions src/PropsControl/Exceptions/InvalidState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare (strict_types = 1);

namespace Wavevision\PropsControl\Exceptions;

use Exception;

class InvalidState extends Exception
{

}
36 changes: 36 additions & 0 deletions src/PropsControl/ProcessedProps.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare (strict_types = 1);

namespace Wavevision\PropsControl;

use Nette\SmartObject;

/**
* @internal
*/
final class ProcessedProps
{

use SmartObject;

/**
* @var mixed[]
*/
private $values = [];

/**
* @param mixed $value
*/
public function __set(string $name, $value): void
{
$this->values[$name] = $value;
}

/**
* @return mixed[]
*/
public function getValues(): array
{
return $this->values;
}

}
21 changes: 14 additions & 7 deletions src/PropsControl/Props.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,30 @@ public function __construct(array $data = [])
{
$this->data = $data;
$this->processor = new Processor();
$this->schema = Expect::structure($this->define());
$this->schema = Expect::structure($this->define())->castTo(ProcessedProps::class);
}

/**
* @return mixed[]
*/
public function getData(): array
{
return $this->data;
}

/**
* @param mixed[]|null $data
* @return object
*/
public function process(?array $data = null): object
final public function process(?array $data = null): ValidProps
{
if ($data === null) {
$data = $this->data;
}
return $this->processor->process($this->schema, $data);
/** @var ProcessedProps $props */
$props = $this->processor->process($this->schema, $data ?: $this->getData());
return (new ValidProps($props->getValues()))->setProps($this);
}

/**
* @return Schema[]
*/
abstract protected function define(): array;

}
56 changes: 41 additions & 15 deletions src/PropsControl/PropsControl.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace Wavevision\PropsControl;

use Nette\Bridges\ApplicationLatte\Template;
use Nette\InvalidArgumentException;
use Nette\InvalidStateException;
use Wavevision\PropsControl\Exceptions\InvalidProps;
use Wavevision\PropsControl\Exceptions\InvalidState;
use Wavevision\Utils\Strings;

/**
Expand Down Expand Up @@ -49,7 +49,7 @@ public function getControlName(): string
}

/**
* @param object|mixed[] $props
* @param mixed[]|object $props
*/
public function render($props): void
{
Expand All @@ -58,7 +58,7 @@ public function render($props): void
}

/**
* @param object|mixed[] $props
* @param mixed[]|object $props
* @return string
*/
public function renderToString($props): string
Expand All @@ -67,11 +67,11 @@ public function renderToString($props): string
return $this->template->renderToString();
}

protected function beforeMapPropsToTemplate(object $props): void
protected function beforeMapPropsToTemplate(ValidProps $props): void
{
}

protected function beforeRender(object $props): void
protected function beforeRender(ValidProps $props): void
{
}

Expand All @@ -90,21 +90,19 @@ final protected function getMappedModifiers(): array
final protected function getMappedProp(string $prop)
{
if ($props = $this->getMappedProps()) {
return $props->$prop ?? null;
return $props->getNullable($prop);
}
return null;
}

final protected function getMappedProps(): ?object
final protected function getMappedProps(): ?ValidProps
{
return $this->template->{self::PROPS} ?? null;
}

final protected function mapPropsToTemplate(object $props): void
{
if ($props instanceof Props) {
$props = $props->process();
}
$props = $this->validateProps($props);
$this->beforeMapPropsToTemplate($props);
$this->template->{self::PROPS} = $props;
$this->template->{self::MODIFIERS} = [];
Expand Down Expand Up @@ -135,9 +133,7 @@ final protected function prepareRender($props): void
$props = $this->createProps($props);
}
if (!is_object($props)) {
throw new InvalidArgumentException(
sprintf('Render props must be array|object, "%s" given to "%s".', gettype($props), static::class)
);
throw $this->createInvalidProps('Render props must be array|object', $props);
}
$this->mapPropsToTemplate($props);
}
Expand All @@ -160,8 +156,38 @@ private function createProps(array $props): Props
{
$class = $this->getPropsClass();
if (!class_exists($class)) {
throw new InvalidStateException("Props definition '$class' does not exist.");
throw new InvalidState("Props definition '$class' does not exist.");
}
return new $class($props);
}

/**
* @param mixed[]|object $props
*/
private function createInvalidProps(string $message, $props): InvalidProps
{
return new InvalidProps(
sprintf(
'%s, "%s" given to "%s".',
$message,
gettype($props),
static::class
)
);
}

private function validateProps(object $props): ValidProps
{
if ($props instanceof Props) {
$props = $props->process();
}
if (!($props instanceof ValidProps)) {
throw $this->createInvalidProps(
sprintf('Mapped props must be an instance of "%s"', ValidProps::class),
$props
);
}
return $props;
}

}
3 changes: 2 additions & 1 deletion src/PropsControl/PropsControlTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class PropsControlTemplate extends Template
public $modifiers;

/**
* @var object
* @var ValidProps
*/
public $props;

}
78 changes: 78 additions & 0 deletions src/PropsControl/ValidProps.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php declare (strict_types = 1);

namespace Wavevision\PropsControl;

use Nette\MemberAccessException;
use Nette\SmartObject;
use stdClass;

/**
* @internal
*/
final class ValidProps extends stdClass
{

use SmartObject;

/**
* @var Props|null
*/
private $props;

/**
* @var mixed[]
*/
private $values;

/**
* @param mixed[] $values
*/
public function __construct(array $values)
{
$this->values = $values;
}

/**
* @return mixed
*/
public function &__get(string $name)
{
if (!$this->isSet($name)) {
throw new MemberAccessException("Prop '$name' does on exist in validated props object.");
}
return $this->values[$name];
}

/**
* @return mixed
*/
public function get(string $prop)
{
return $this->values[$prop];
}

/**
* @return mixed|null
*/
public function getNullable(string $prop)
{
return $this->isSet($prop) ? $this->get($prop) : null;
}

public function isSet(string $prop): bool
{
return array_key_exists($prop, $this->values);
}

public function getProps(): ?Props
{
return $this->props;
}

public function setProps(Props $props): self
{
$this->props = $props;
return $this;
}

}
1 change: 1 addition & 0 deletions tests/PropsControlTests/ClassNameTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ function () use ($modifiers): array {
}
);
}

}
1 change: 1 addition & 0 deletions tests/PropsControlTests/Components/InvalidComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ protected function getPropsClass(): string
{
return '';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Wavevision\PropsControlTests\Components\TestComponent;

use Wavevision\PropsControl\PropsControl;
use Wavevision\PropsControl\ValidProps;

class TestComponent extends PropsControl
{
Expand All @@ -26,10 +27,10 @@ public function getClassNameModifiers(): array
TestComponentProps::BOOLEAN_VALUE,
// Use 'type' prop value as a modifier
TestComponentProps::TYPE => self::USE_VALUE,
// Define custom modifier, $modifier => callback(object $props), if truthy, modifier will be used...
'custom' => function (object $props): bool {
// Define custom modifier, $modifier => callback(ValidProps $props), if truthy, modifier will be used...
'custom' => function (ValidProps $props): bool {
// $props have been validated, we're accessing nullable prop
if ($entity = $props->{TestComponentProps::ENTITY}) {
if ($entity = $props->get(TestComponentProps::ENTITY)) {
return $entity->enabled;
}
return false;
Expand All @@ -41,14 +42,14 @@ function (): string {
];
}

protected function beforeMapPropsToTemplate(object $props): void
protected function beforeMapPropsToTemplate(ValidProps $props): void
{
parent::beforeMapPropsToTemplate($props);
// do stuff before valid props are sent to template, e.g. assign extra params to template
$this->template->setParameters(['undefinedProp' => $this->getMappedProp('undefinedProp')]);
}

protected function beforeRender(object $props): void
protected function beforeRender(ValidProps $props): void
{
parent::beforeRender($props);
// do stuff before component is rendered
Expand All @@ -58,4 +59,5 @@ protected function getPropsClass(): string
{
return TestComponentProps::class;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ protected function define(): array
self::TYPE => Expect::anyOf(...self::TYPES)->default(self::TYPE_ONE),
];
}

}
Loading

0 comments on commit 05d2876

Please sign in to comment.