Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v 1.0.2 #4

Merged
merged 4 commits into from
Dec 19, 2024
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
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.
Loading