Skip to content

Commit

Permalink
Merge pull request #4 from prosopo/main
Browse files Browse the repository at this point in the history
v 1.0.2
  • Loading branch information
light-source authored Dec 19, 2024
2 parents 9fd52c2 + 5c8db7f commit 4315d2a
Show file tree
Hide file tree
Showing 12 changed files with 543 additions and 125 deletions.
478 changes: 365 additions & 113 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prosopo/views",
"description": "Blazing fast Views with model-driven approach, multi-namespace support and custom Blade implementation as a default template engine.",
"description": "Blazing fast Views with model-driven approach, Blade and multi-namespace support.",
"homepage": "https://github.com/prosopo/php-views",
"keywords": [
"Views",
Expand Down
3 changes: 2 additions & 1 deletion private-classes/Model/ModelFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Prosopo\Views\PrivateClasses\Model;

use Closure;
use Prosopo\Views\Interfaces\Model\ModelFactoryInterface;
use Prosopo\Views\Interfaces\Object\ObjectReaderInterface;
use Prosopo\Views\Interfaces\Object\PropertyValueProviderInterface;
Expand Down Expand Up @@ -33,7 +34,7 @@ public function __construct(
$this->templateRenderer = $templateRenderer;
}

public function createModel(string $modelClass)
public function createModel(string $modelClass, ?Closure $setupModelCallback = null)
{
return new $modelClass(
$this->objectReader,
Expand Down
3 changes: 2 additions & 1 deletion private-classes/Model/ModelFactoryWithDefaultsManagement.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Prosopo\Views\PrivateClasses\Model;

use Closure;
use Prosopo\Views\Interfaces\Model\ModelFactoryInterface;
use Prosopo\Views\Interfaces\Model\TemplateModelWithDefaultsInterface;
use Prosopo\Views\Interfaces\Object\ObjectPropertyWriterInterface;
Expand All @@ -29,7 +30,7 @@ public function __construct(
$this->objectPropertyWriter = $objectPropertyWriter;
}

public function createModel(string $modelClass)
public function createModel(string $modelClass, ?Closure $setupModelCallback = null)
{
$model = $this->modelFactory->createModel($modelClass);

Expand Down
33 changes: 33 additions & 0 deletions private-classes/Model/ModelFactoryWithSetupCallback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Prosopo\Views\PrivateClasses\Model;

use Closure;
use Prosopo\Views\Interfaces\Model\ModelFactoryInterface;

/**
* This class is marked as a final and placed under the 'Private' namespace to prevent anyone from using it directly.
* We reserve the right to change its name and implementation.
*/
final class ModelFactoryWithSetupCallback implements ModelFactoryInterface
{
private ModelFactoryInterface $modelFactory;

public function __construct(ModelFactoryInterface $modelFactory)
{
$this->modelFactory = $modelFactory;
}

public function createModel(string $modelClass, ?Closure $setupModelCallback = null)
{
$model = $this->modelFactory->createModel($modelClass);

if (null !== $setupModelCallback) {
$setupModelCallback($model);
}

return $model;
}
}
2 changes: 1 addition & 1 deletion private-classes/Model/ModelRendererWithEventDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function __construct(
$this->eventName = $eventName;
}

public function renderModel($modelOrClass, Closure $setupModelCallback = null): string
public function renderModel($modelOrClass, ?Closure $setupModelCallback = null): string
{
$modelClass = true === is_string($modelOrClass) ?
$modelOrClass :
Expand Down
3 changes: 3 additions & 0 deletions private-classes/View/ViewNamespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
PropertyValueProviderForNullable};
use Prosopo\Views\PrivateClasses\Model\{ModelFactory,
ModelFactoryWithDefaultsManagement,
ModelFactoryWithSetupCallback,
ModelNameResolver,
ModelNamespaceResolver,
ModelRenderer,
Expand Down Expand Up @@ -139,6 +140,8 @@ public function __construct(
$objectPropertyWriter
);

$realModelFactory = new ModelFactoryWithSetupCallback($realModelFactory);

$realModelRenderer = $modules->getModelRenderer();
$realModelRenderer = null === $realModelRenderer ?
new ModelRenderer(
Expand Down
4 changes: 3 additions & 1 deletion src/Interfaces/Model/ModelFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Prosopo\Views\Interfaces\Model;

use Closure;
use Exception;

interface ModelFactoryInterface
Expand All @@ -12,10 +13,11 @@ interface ModelFactoryInterface
* @template T of TemplateModelInterface
*
* @param class-string<T> $modelClass
* @param Closure(T):void|null $setupModelCallback
*
* @return T
*
* @throws Exception
*/
public function createModel(string $modelClass);
public function createModel(string $modelClass, ?Closure $setupModelCallback = null);
}
2 changes: 1 addition & 1 deletion src/Interfaces/Model/ModelRendererInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ interface ModelRendererInterface
*
* @throws Exception
*/
public function renderModel($modelOrClass, Closure $setupModelCallback = null): string;
public function renderModel($modelOrClass, ?Closure $setupModelCallback = null): string;
}
6 changes: 3 additions & 3 deletions src/ViewsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function registerNamespace(string $namespace, ViewNamespaceConfig $config
return $viewNamespaceModules;
}

public function createModel(string $modelClass)
public function createModel(string $modelClass, ?Closure $setupModelCallback = null)
{
if (false === $this->isModel($modelClass)) {
throw $this->makeWrongModelException($modelClass);
Expand All @@ -80,10 +80,10 @@ public function createModel(string $modelClass)
throw $this->makeNamespaceNotResolvedException($modelNamespace);
}

return $modelFactory->createModel($modelClass);
return $modelFactory->createModel($modelClass, $setupModelCallback);
}

public function renderModel($modelOrClass, Closure $setupModelCallback = null): string
public function renderModel($modelOrClass, ?Closure $setupModelCallback = null): string
{
if (false === $this->isModel($modelOrClass)) {
throw $this->makeWrongModelException($modelOrClass);
Expand Down
132 changes: 129 additions & 3 deletions tests/pest/Feature/ViewsManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Tests\Feature;

use Closure;
use org\bovigo\vfs\vfsStream;
use ParseError;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -517,6 +518,52 @@ public function testRenderSupportsInnerModelPrintWhenModelExtendsTheBaseClass():
$this->assertSame('Hey inner!', $viewsManager->renderModel($topModel));
}

public function testRenderPassesArrayOfInnerModelsAsObjects(): void
{
// given
vfsStream::setup('top', null, [
'folder1' => ['top-model.blade.php' => 'Hey {{ true === is_object($inners[0])? "inner object!": "string" }}'],
'folder2' => [ 'inner-model.blade.php' => 'inner!'],
]);
$bladeRenderer = new ViewTemplateRenderer();
$firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer))
->setTemplatesRootPath(vfsStream::url('top/folder1'))
->setTemplateFileExtension('.blade.php');
$secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer))
->setTemplatesRootPath(vfsStream::url('top/folder2'))
->setTemplateFileExtension('.blade.php');
$secondNamespace = $this->defineRealModelClass(
__METHOD__ . '__second',
'InnerModel',
[],
false
);
$firstNamespace = $this->defineRealModelClass(
__METHOD__,
'TopModel',
[
[
'name' => 'inners',
'visibility' => 'public',
]
],
false
);
$views = new ViewsManager();

// when
$views->registerNamespace($firstNamespace, $firstNamespaceConfig);
$views->registerNamespace($secondNamespace, $secondNamespaceConfig);

$innerModelClass = $secondNamespace . '\\InnerModel';
$topModelClass = $firstNamespace . '\\TopModel';
$topModel = new $topModelClass();
$topModel->inners = [new $innerModelClass()];

// then
$this->assertSame('Hey inner object!', $views->renderModel($topModel));
}

public function testRenderPassesInnerModelsAsStringsWhenFlagIsSet(): void
{
// given
Expand Down Expand Up @@ -564,6 +611,53 @@ public function testRenderPassesInnerModelsAsStringsWhenFlagIsSet(): void
$this->assertSame('Hey inner!', $views->renderModel($topModel));
}

public function testRenderPassesArrayOfInnerModelsAsStringsWhenFlagIsSet(): void
{
// given
vfsStream::setup('top', null, [
'folder1' => ['top-model.blade.php' => 'Hey @foreach ($inners as $inner){!! $inner !!}@endforeach'],
'folder2' => [ 'inner-model.blade.php' => 'inner!'],
]);
$bladeRenderer = new ViewTemplateRenderer();
$firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer))
->setTemplatesRootPath(vfsStream::url('top/folder1'))
->setTemplateFileExtension('.blade.php')
->setModelsAsStringsInTemplates(true);
$secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer))
->setTemplatesRootPath(vfsStream::url('top/folder2'))
->setTemplateFileExtension('.blade.php');
$secondNamespace = $this->defineRealModelClass(
__METHOD__ . '__second',
'InnerModel',
[],
false
);
$firstNamespace = $this->defineRealModelClass(
__METHOD__,
'TopModel',
[
[
'name' => 'inners',
'visibility' => 'public',
]
],
false
);
$views = new ViewsManager();

// when
$views->registerNamespace($firstNamespace, $firstNamespaceConfig);
$views->registerNamespace($secondNamespace, $secondNamespaceConfig);

$innerModelClass = $secondNamespace . '\\InnerModel';
$topModelClass = $firstNamespace . '\\TopModel';
$topModel = new $topModelClass();
$topModel->inners = [new $innerModelClass()];

// then
$this->assertSame('Hey inner!', $views->renderModel($topModel));
}

public function testRenderSupportsInnerModelsFromDifferentNamespaces(): void
{
// given
Expand Down Expand Up @@ -733,6 +827,38 @@ public function testMakeModelImplementsInterface(): void
$this->assertSame($modelClass, get_class($model));
}

public function testMakeModelCallsSetupCallback(): void
{
// given
$bladeRenderer = new ViewTemplateRenderer();
$namespaceConfig = (new ViewNamespaceConfig($bladeRenderer));
$views = new ViewsManager();

$modelNamespace = $this->defineRealModelClass(
__METHOD__,
'FirstModel',
[
[
'name' => 'message',
'type' => 'string',
'visibility' => 'public',
]
],
false
);

// when
$views->registerNamespace($modelNamespace, $namespaceConfig);

$modelClass = $modelNamespace . '\\FirstModel';
$model = $views->createModel($modelClass, function (object $model) {
$model->message = 'Hello World!';
});

// then
$this->assertSame("Hello World!", $model->message);
}

public function testMakeModelSetsDefaultsForModelsThatExtendBaseClass(): void
{
// given
Expand Down Expand Up @@ -800,7 +926,7 @@ public function testMakeModelSupportsDifferentNamespaces(): void

$firstNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer));
$firstNamespaceConfig->getModules()->setModelFactory(new class implements ModelFactoryInterface{
public function createModel(string $modelClass): object
public function createModel(string $modelClass, ?Closure $setupModelCallback = null): object
{
$model = new $modelClass();

Expand All @@ -812,7 +938,7 @@ public function createModel(string $modelClass): object

$secondNamespaceConfig = (new ViewNamespaceConfig($bladeRenderer));
$secondNamespaceConfig->getModules()->setModelFactory(new class implements ModelFactoryInterface{
public function createModel(string $modelClass): object
public function createModel(string $modelClass, ?Closure $setupModelCallback = null): object
{
$model = new $modelClass();

Expand Down Expand Up @@ -869,7 +995,7 @@ public function testMakeModelWithDefaultsSupportsDifferentNamespaces(): void
$firstNamespaceConfig
->getModules()
->setModelFactory(new class implements ModelFactoryInterface{
public function createModel(string $modelClass): object
public function createModel(string $modelClass, ?Closure $setupModelCallback = null): object
{
$model = new $modelClass();

Expand Down
Empty file.

0 comments on commit 4315d2a

Please sign in to comment.