Phalcon: HMVC view not working - view

I got a problem rendering nested view, here is what I'm trying to do
I changed your 'request' of HMVC (HMVC-GitHub or/and HMVC-Pattern) function into an Elements module
namespace Modules\Main\Libraries;
/**
* Elements
*
* Helps to build UI elements for the application
*/
class Elements extends \Phalcon\Mvc\User\Component
{
public function loadModule($path = '', $data = array()) {
$di = clone $this->getDI();
$dispatcher = $di->get('dispatcher');
$paths = explode('/', $path);
$data = is_array($data) ? $data : array($data);
// get controller name
if (isset($paths[0])) {
$controller = $paths[0];
}
// get action name
if (isset($paths[1])) {
$action = $paths[1];
}
// get params
if (isset($paths[2])) {
array_splice($paths, 0, 2);
$params = array_merge($paths, $data);
} else {
$params = $data;
}
if (!empty($controller)) {
$dispatcher->setControllerName($controller);
} else {
$dispatcher->setControllerName('index');
}
if (!empty($action)) {
$dispatcher->setActionName($action);
} else {
$dispatcher->setActionName('index');
}
if (!empty($params)) {
if(is_array($params)) {
$dispatcher->setParams($params);
} else {
$dispatcher->setParams((array) $params);
}
} else {
$dispatcher->setParams(array());
}
$dispatcher->dispatch();
$response = $dispatcher->getReturnedValue();
if ($response instanceof ResponseInterface) {
return $response->getContent();
}
return $response;
}
}
and I have 2 controllers:
namespace Modules\Main\Controllers;
class IndexController extends ControllerBase
{
public function indexAction()
{
$secondContent = $this->elements->loadModule('test/hello/json');
$this->view->setVar('secondContent', $secondContent);
}
}
and
namespace Modules\Main\Controllers;
use \Phalcon\Http\Response;
class TestController extends ControllerBase
{
public function indexAction()
{
}
public function helloAction($format='html', $param = 'empty')
{
$this->view->setVar('content', 'Hello this is test value "'.$param.'"');
$content = $this->view->getContent();
return (string)$content;
// return 'Hello this is test value "'.$param.'"';
}
}
my DI
$di['elements'] = function() {
return new \Modules\Main\Libraries\Elements();
};
Views files
IndexController::Index
<h1>Congratulations!</h1>
<p>You're now flying with Phalcon. Great things are about to happen!</p>
<p>Second content: {{ secondContent}}</p>
<p>HMVC: {{ elements.loadModule('test/hello/json', 'test') }}</p>
and HelloController::test
This is :: {{ content }}
expecting to get
Congratulations!
You're now flying with Phalcon. Great things are about to happen!
Second content: This is :: Hello this is test value "empty"
HMVC: This is :: Hello this is test value "test"
but it only rendering the HelloController (First call from IndexController::indexAction):
This is :: Hello this is test value "empty"
if I change IndexController::indexAction to
public function indexAction()
{
$secondContent = '';
$this->view->setVar('secondContent', $secondContent);
}
and TestController::helloAction to
public function helloAction($format='html', $param = 'empty')
{
$this->view->setVar('content', 'Hello this is test value "'.$param.'"');
$content = $this->view->getContent();
//return (string) $content;
return 'Hello this is test value "'.$param.'"';
}
the result that i get is (Second content is empty):
Congratulations!
You're now flying with Phalcon. Great things are about to happen!
Second content:
HMVC: Hello this is test value "test"
Any solution to solve this ?
Thanks,
Helman

Phalcon have built-it modules feature, you dont have to built your own module loader, you just need create module bootstrap that extend ModuleDefinitionInterface.
Just take a look this sample from phalcon multi module
https://github.com/phalcon/mvc/tree/master/multiple
this example below is taken from link above, This contain module bootstrap code.
<?php
namespace Multiple\Frontend;
class Module
{
public function registerAutoloaders()
{
$loader = new \Phalcon\Loader();
$loader->registerNamespaces(array(
'Multiple\Frontend\Controllers' => '../apps/frontend/controllers/',
'Multiple\Frontend\Models' => '../apps/frontend/models/',
));
$loader->register();
}
/**
* Register the services here to make them general or register in the ModuleDefinition to make them module-specific
*/
public function registerServices($di)
{
//Registering a dispatcher
$di->set('dispatcher', function () {
$dispatcher = new \Phalcon\Mvc\Dispatcher();
//Attach a event listener to the dispatcher
$eventManager = new \Phalcon\Events\Manager();
$eventManager->attach('dispatch', new \Acl('frontend'));
$dispatcher->setEventsManager($eventManager);
$dispatcher->setDefaultNamespace("Multiple\Frontend\Controllers\\");
return $dispatcher;
});
//Registering the view component
$di->set('view', function () {
$view = new \Phalcon\Mvc\View();
$view->setViewsDir('../apps/frontend/views/');
return $view;
});
$di->set('db', function () {
return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
));
});
}
}
you can load module using this code below
$app = new \Phalcon\Mvc\Application();
$app->registerModules(array(
'frontend' => array(
'className' => 'Multiple\Frontend\Module',
'path' => '../apps/frontend/Module.php'
),
'backend' => array(
'className' => 'Multiple\Backend\Module',
'path' => '../apps/backend/Module.php'
)
));

Related

Laravel Create a request internally Resolved

I need to recreate a resquest so that it behaves like a call via api to go through the validator, but my $request->input('rps.number') always arrives empty, although I can see the data in the debug
I also couldn't get it to go through the laravel validator
I can't use a technique to make an http call, because I need to put this call in a transaction
<?php
$nota = new stdClass();
$rps = new stdClass();
$rps->numero = (int)$xml->Rps->IdentificacaoRps->Numero;
$rps->serie = (string)$xml->Rps->IdentificacaoRps->Serie;
$rps->tipo = (int)$xml->Rps->IdentificacaoRps->Tipo;
$nota->rps = $rps;
$controller = new NotaController(new Nota());
$content = new StoreNotaRequest();
$content->request->add($nota);
$result = $controller->store($content);
StoreNotaRequest
<?php
class StoreNotaRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$request = $this->request;
return [
'rps.numero' => 'required_with:rps|numeric|between:1,999999999999999',
'rps.serie' => 'required_with:rps|string|min:1|max:5',
'rps.tipo' => 'required_with:rps|integer|in:1,2,3'
];
}
}
NotaController
<?php
class NotaController extends Controller
{
private Nota $nota;
public function __construct(Nota $nota)
{
$this->nota = $nota;
}
public function store(StoreNotaRequest $request): JsonResponse
{
// $validated = $request->validated();
try {
$nota = DB::transaction(function () use ($request) {
$request->input('rps.numero');
});
return response()->json($nota);
} catch (Throwable $e) {
return response()->json($data, 409);
}
}
}
Solution
the solution was a little too verbose, I believe it is possible to solve with less code.
more does what it needs to go through the validation of the data contained in the StoreNotaRequest
and it returns an http response, in addition to being able to put all these isolated calls in a single transaction
DB::beginTransaction();
$errors = [];
foreach ($itens as $item) {
$controller = new NotaController(new Nota());
$request = new StoreNotaRequest();
$request->setMethod('POST');
$request->request->add($nota);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
$response = $controller->store($request);
if ($response->statusText() !== 'OK') {
$errors[$item->id] = 'ERROR';
}
}
if (count($errors) === 0) {
DB::commit();
} else {
DB::rollBack();
}

SwaggerDecorator not working after update API Platform to v2.3.5

After the API platform upgrade, the decorator from the documentation has stopped working:
https://api-platform.com/docs/core/swagger/#overriding-the-swagger-documentation
Does anyone know if this is a change, is it a bug?
I use Symfony 4.2.2 (probably the problem is due to the Symfony update).
My code adding to swagger input form to change context:
<?php
namespace App\Swagger;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
final class SwaggerDecorator implements NormalizerInterface
{
private $decorated;
public function __construct(NormalizerInterface $decorated)
{
$this->decorated = $decorated;
}
public function normalize($object, $format = null, array $context = [])
{
$docs = $this->decorated->normalize($object, $format, $context);
$customDefinition = [
'name' => 'context',
'definition' => 'Context field',
'default' => '',
'in' => 'query',
];
// Add context parameter
foreach ($docs['paths'] as $key => $value) {
// e.g. add a custom parameter
$customDefinition['default'] = lcfirst($docs['paths'][$key]['get']['tags'][0] ?? '');
$docs['paths'][$key]['get']['parameters'][] = $customDefinition;
if(isset($docs['paths'][$key]['post'])){
$docs['paths'][$key]['post']['parameters'][] = $customDefinition;
}
if(isset($docs['paths'][$key]['put'])){
$docs['paths'][$key]['put']['parameters'][] = $customDefinition;
}
}
return $docs;
}
public function supportsNormalization($data, $format = null)
{
return $this->decorated->supportsNormalization($data, $format);
}
}
Try to use parameter "decoration_priority" in services configuration (https://symfony.com/doc/current/service_container/service_decoration.html#decoration-priority)
For example:
App\Swagger\SwaggerDecorator:
decorates: 'api_platform.swagger.normalizer.documentation'
arguments: [ '#App\Swagger\SwaggerDecorator.inner' ]
decoration_priority: 1000
Or fix version "symfony/dependency-injection": "4.2.1" in composer.json )
See https://github.com/symfony/symfony/issues/29836 for details

Yii2 - dynamically switch rules set in model

I want to dynamically substitute rules in model according to switch value on form.
In view:
<?php
$form = ActiveForm::begin([
'enableAjaxValidation' => true,
'validationUrl' => Url::toRoute('anounce/validation')
]);
?>
In controller:
public function actionValidation()
{
$model = new Anounce();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->
request->post())) {
Yii::$app->response->format = 'json';
return ActiveForm::validate($model);
}
}
Excerpts from model:
class Anounce extends \yii\db\ActiveRecord
{
private $currentRuleSet; // Current validation set
// Here are arrays of rules with assignment
private $fullRuleSet; // = [...];
private $shortRuleSet; // = [...];
private $minRuleSet; // = [...];
public function init()
{
parent::init();
$this->currentRuleSet = $this->fullRuleSet;
}
public function rules()
{
return $this->currentRuleSet;
}
public function beforeValidate()
{
if ($this->idanounce_type === self::FULL) {
$this->currentRuleSet = $this->fullRuleSet;
} else if ($this->idanounce_type === self::SHORTER) {
$this->currentRuleSet = $this->shortRuleSet;
} else if ($this->idanounce_type === self::MINIMAL) {
$this->currentRuleSet = $this->minRuleSet;
}
return parent::beforeValidate();
}
}
Variable idanounce_type is a switch between rules.
Unfortunately, validation made according to full rules set (or rules set used in init), despite on which *RuleSet value assigned to currentRuleSet.
How to write dynamic switching of rules?
What you want here is to change validation according to the user's input. You can do this by defining scenarios in your model.
So firstly set scenarios where you put in it the fields that are to be validated. Example if you have username, password, and email fields, and you defined two scenarios, in SCENARIO_FIRST only username and password will get validated.
public function scenarios()
{
return [
self::SCENARIO_FIRST => ['username', 'password'],
self::SCENARIO_SECOND => ['username', 'email', 'password'],
];
}
Then in your controller, set the scenario according to the input:
public function actionValidation()
{
$model = new Anounce();
//example
if($condition == true)
{
$model->scenario = Anounce::SCENARIO_FIRST;
}
if (Yii::$app->request->isAjax && $model->load(Yii::$app->
request->post())) {
Yii::$app->response->format = 'json';
return ActiveForm::validate($model);
}
}
Read more about scenarios here and how to use them with validation here:
http://www.yiiframework.com/doc-2.0/guide-structure-models.html#scenarios

Phalcon validation scenario

I used to use Yii framework. I would like to make project using Phalcon. I could not find validation scenario on Phalcon. What is the best way to correctly implement it on Phalcon?
Thanks in advance.
Any data validation:
<?php
use Phalcon\Validation\Validator\PresenceOf,
Phalcon\Validation\Validator\Email;
$validation = new Phalcon\Validation();
$validation->add('name', new PresenceOf(array(
'message' => 'The name is required'
)));
$validation->add('email', new PresenceOf(array(
'message' => 'The e-mail is required'
)));
$validation->add('email', new Email(array(
'message' => 'The e-mail is not valid'
)));
$messages = $validation->validate($_POST);
if (count($messages)) {
foreach ($messages as $message) {
echo $message, '<br>';
}
}
http://docs.phalconphp.com/en/1.2.6/reference/validation.html
If you are working with models:
<?php
use Phalcon\Mvc\Model\Validator\InclusionIn,
Phalcon\Mvc\Model\Validator\Uniqueness;
class Robots extends \Phalcon\Mvc\Model
{
public function validation()
{
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("Mechanical", "Virtual")
)
));
$this->validate(new Uniqueness(
array(
"field" => "name",
"message" => "The robot name must be unique"
)
));
return $this->validationHasFailed() != true;
}
}
http://docs.phalconphp.com/en/1.2.6/reference/models.html#validating-data-integrity
models also have events, so you can add any logic you need in these functions:
http://docs.phalconphp.com/en/1.2.6/reference/models.html#events-and-events-manager
I would like to use forms for CRUD as they are very dynamic and reusable.
You can achieve that in forms using options.
You can pass additional options to form and act like a scenario.
You can check Form constructor here
https://docs.phalconphp.com/en/latest/api/Phalcon_Forms_Form.html
In your controller you can pass $options
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function insertAction()
{
$options = array();
$options['scenario'] = 'insert';
$myForm = new MyForm(null, $options);
if($this->request->hasPost('insert')) {
// this will be our model
$profile = new Profile();
// we will bind model to form to copy all valid data and check validations of forms
if($myForm->isValid($_POST, $profile)) {
$profile->save();
}
else {
echo "<pre/>";print_r($myForm->getMessages());exit();
}
}
}
public function updateAction()
{
$options = array();
$options['scenario'] = 'update';
$myForm = new MyForm(null, $options);
}
}
And your form should look like something this
<?php
// elements
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
// validators
use Phalcon\Validation\Validator\PresenceOf;
class MyForm extends Form {
public function initialize($entity = null, $options = null) {
$name = new Text('first_name');
$this->add($name);
if($options['scenario'] == 'insert') {
// at the insertion time name is required
$name->addValidator(new PresenceOf(array('message' => 'Name is required.')));
}
else {
// at the update time name is not required
// as well you can add more additional validations
}
}
}
now you can add multiple scenarios and act based on scenarios.

How to ajaxify Zend_Form validation

I have a Zend_Form subclass. Some elements are set to belong to arrays.
class My_Form extends Zend_Form {
public function __construct() {
$elem = $this->createElement('text','PROJECT_NAME',
array(
'required' => true
));
$elem->setBelongsTo('project');
$this->addElement($elem);
$elem = $this->createElement(
'text','PLANNED_END_DATE',
array(
'required' => true
)
);
$elem->setBelongsTo('project');
$elem->addValidator(new Zend_Validate_Date(array('format'=>'yyyy-MM-dd')));
$this->addElement($elem);
//and so on
}
}
I have a universal validation controller which does create the form and checks for errors, and returns them in json format:
class ValidateController extends Zend_Controller_Action
{
public function formAction()
{
$params = $this->_getAllParams();
if (isset($params['_frm'])) {
$formName = detect_the_form_class($params['_frm']);
if (class_exists($formName)) {
$form = new $formName();
if ($form instanceof Zend_Form) {
$result = $form->isValidPartial($params);
$messages = $form->getMessages();
$this->getResponse()
->setHeader('Content-Type', 'application/json')
->setBody(json_encode(array(
'result' => $result,
'messages' => $messages
)));
} else {
$this->getResponse()->setHttpResponseCode(500);
}
}
}
}
}
This controller works great for non-array forms, but the form I now need to validate hase arrays, eg elements with name 'project[PROJECT_NAME]'.
But the $form->getMessages() returns messages indexed with base name of elements, without array prefix.
The actual result is:
{ result: false,
messages: {
PROJECT_NAME: {isEmpty: "Value is required"},
PROJECT_END_DATE: {isEmpty: "Value is required"}
}
}
The result I need is:
{ result: false,
messages: {
"project[PROJECT_NAME]": {isEmpty: "Value is required"},
"project[PROJECT_END_DATE]": {isEmpty: "Value is required"}
}
}
or something similar, so I can find the element the validation message is for.
Any ideas?
For subforms use Zend_Form_Subform class:
class My_Form extends Zend_Form
{
public function init()
{
$subForm = new Zend_Form_SubForm();
$elem = $this->createElement('text', 'PROJECT_NAME', array(
'required' => true
));
$subForm->addElement($elem);
$elem = $this->createElement('text', 'PLANNED_END_DATE', array(
'required' => true
));
$subForm->addElement($elem);
$this->addSubForm($subForm, 'project');
}
}
Response:
{
"project":{
"PROJECT_NAME":{"isEmpty":"Value is required and can't be empty"},
"PLANNED_END_DATE":{"isEmpty":"Value is required and can't be empty"}
}
}
For form config it is recommended to use init() method.
For json response you can use build-in action helper:
$this->_helper->json($form->getMessages());

Resources