Is there a cakephp component to create full APIs? - json

I'm trying to build a RESTful api for multiple models.
I found this:
class RecipesController extends AppController {
public $components = array('RequestHandler');
public function index() {
$recipes = $this->Recipe->find('all');
$this->set(array(
'recipes' => $recipes,
'_serialize' => array('recipes')
));
}
public function view($id) {
$recipe = $this->Recipe->findById($id);
$this->set(array(
'recipe' => $recipe,
'_serialize' => array('recipe')
));
}
public function edit($id) {
$this->Recipe->id = $id;
if ($this->Recipe->save($this->request->data)) {
$message = 'Saved';
} else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
public function delete($id) {
if ($this->Recipe->delete($id)) {
$message = 'Deleted';
} else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
}
But I'm trying to avoid duplicating all those methods in each controller that I need to create. I've been looking for a component that will do all the CRUD operations that I can include in each controller.
1) Does such a component exist?
2) Am I overkilling this thing when I should just duplicate this basic controller for each model?

I assume your best bet would be this:
https://github.com/FriendsOfCake/crud
"CakePHP Application development on steroids - rapid prototyping / scaffolding & production ready code - JSON APIs and more"

Related

Download CSV in Mezzio Framework (Zend/Laminas)

In Mezzion Framework I have the next Handler:
<?php
namespace Bgc\Handler;
use App\Service\GenerateReportToCSV;
use Bgc\Queue\BGCQueueManager;
use Laminas\Diactoros\Response\TextResponse;
use League\Csv\Writer;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class DownloadBgcReportHandler implements RequestHandlerInterface
{
protected $bgcQManager;
protected $reportToCSV;
public function __construct(BGCQueueManager $bgcQManager, $reportToCSV)
{
$this->bgcQManager = $bgcQManager;
$this->reportToCSV = $reportToCSV;
}
public function handle(ServerRequestInterface $request): TextResponse
{
$queryParams = $request->getQueryParams();
$params = [];
if (isset($queryParams['startDate'])) {
$starDate = new \DateTime($queryParams['startDate']);
$params['startDate'] = $starDate->modify('midnight');
}
if (isset($queryParams['startDate'])) {
$endDate = new \DateTime($queryParams['endDate']);
$params['endDate'] = $endDate->modify('tomorrow');
}
$itemsBGC = $this->bgcQManager->getDataToDownload($params);
$time = time();
$fileName = "bgc-report-$time.csv";
$csv = Writer::createFromFileObject(new \SplFileObject());
$csv->insertOne($this->reportToCSV->getHeadingsBGC());
foreach ($itemsBGC as $item) {
$csv->insertOne($item);
}
return new TextResponse($csv->getContent(), 200, [
'Content-Type' => 'text/csv',
'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' => "attachment; filename='$fileName'"
]);
}
}
I have the below error:
Whoops\Exception\ErrorException: Declaration of Bgc\Handler\DownloadBgcReportHandler::handle(Psr\Http\Message\ServerRequestInterface $request): Laminas\Diactoros\Response\TextResponse must be compatible with Psr\Http\Server\RequestHandlerInterface::handle(Psr\Http\Message\ServerRequestInterface $request): Psr\Http\Message\ResponseInterface in file /home/peter/proyectos/revelations-thena-api/src/Bgc/src/Handler/DownloadBgcReportHandler.php on line 20
I don't know, to create a downloable file. The hadbler works fine with Json. I tried to change from ResponseInterface to TextResponse.
How can I download file CSV?
Thank you
The error you received is telling you that your method signature is not compliant to interface's method signature.
RequestHandlerInterface:
interface RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): ResponseInterface;
}
As you see, the signature states that an object of type ResponseInterface is returned.
You modified the signature:
class DownloadBgcReportHandler implements RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): TextResponse;
}
The signature must be the same, but then you can return the TextResponse without problem (since it extends Laminas\Diactoros\Response, which implements Psr\Http\Message\ResponseInterface)
Just change that and it will works :)
You have modified you handle method, so right now you aren't fulfilling the requirements of the RequestHandlerInterface
Replace the return value for the handler with ResponseInterface enforced in the interface: RequestHandlerInterface
so i think you are best helped with:
<?php
namespace Bgc\Handler;
use App\Service\GenerateReportToCSV;
use Bgc\Queue\BGCQueueManager;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\Stream;
use League\Csv\Writer;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class DownloadBgcReportHandler implements RequestHandlerInterface
{
protected $bgcQManager;
protected $reportToCSV;
public function __construct(BGCQueueManager $bgcQManager, $reportToCSV)
{
$this->bgcQManager = $bgcQManager;
$this->reportToCSV = $reportToCSV;
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$queryParams = $request->getQueryParams();
$params = [];
if (isset($queryParams['startDate'])) {
$starDate = new \DateTime($queryParams['startDate']);
$params['startDate'] = $starDate->modify('midnight');
}
if (isset($queryParams['startDate'])) {
$endDate = new \DateTime($queryParams['endDate']);
$params['endDate'] = $endDate->modify('tomorrow');
}
$itemsBGC = $this->bgcQManager->getDataToDownload($params);
$time = time();
$fileName = "bgc-report-$time.csv";
// $csv = Writer::createFromFileObject(new \SplFileObject());
// $csv->insertOne($this->reportToCSV->getHeadingsBGC());
$csv = Writer::createFromString($this->reportToCSV->getHeadingsBGC());
foreach ($itemsBGC as $item) {
$csv->insertOne($item);
}
$body = new Stream($csv->getContent());
return new Response($body, 200, [
'Cache-Control' => 'must-revalidate',
'Content-Disposition' => 'attachment; filename=' . $fileName,
'Content-Length' => strval($body->getSize()),
'Content-Type' => 'text/csv',
'Content-Transfer-Encoding' => 'binary',
'Expires' => '0',
'Pragma' => 'public',
]);
}
}
PS: i have commented the 2 lines in which an empty new \SplFileObject() was used, because the required param $filename was empty (and i did not want to make a decision there) and added a line with Writer::createFromString().

Laravel user model not being process in JSON response

I have a Laravel 5.8 API where the JSON response for a user collection works as expected but fails for a model.
namespace App\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
trait ApiResponder
{
private function successResponse($data, $code)
{
return response()->json($data, $code);
}
protected function errorResponse($message, $code)
{
return response()->json(['error' => $message, 'code' => $code], $code);
}
protected function showAll(Collection $collection, $code = 200)
{
return $this->successResponse(['data' => $collection], $code);
}
protected function showOne(Model $model, $code = 200)
{
return $this->successResponse(['data' => $model], $code);
}
}
Below are the controller methods calling for the response.
public function index()
{
$users = User::all();
return $this->showAll($users);
}
public function update(Request $request, $id)
{
$user = User::findOrFail($id);
$rules = [
'email' => 'email|unique:users,email,' . $user->id,
'password' => 'min:6|confirmed'
];
if ($request->has('name')) {
$user->name = $request->name;
}
if ($request->has('email') && $user->email != $request->email) {
$user->verififed = User::UNVERIFIED_USER;
$user->verififcation_token = User::generateVerificationCode();
$user->email = $request->email;
}
if ($request->has('password')) {
$user->password = bcrypt($request->password);
}
if (!$user->isDirty()) {
return $this->errorResponse('You need to specify a change to update', 422);
}
$user->save();
$this->showOne($user);
}
The index method handle as a collection works perfectly, but the update method using the model returns empty (no content at all). I have confirmed that the $data variable does contain the model information as expected as I can print a JSON encode that displays the result I want. It's just not working in response()->json() for some reason.
Very complex code for what it actually does.
Here you have the problem, needless to say to render the response, you need a return.
$user->save();
$this->showOne($user);
}
should be:
$user->save();
return $this->showOne($user);
}
Bonus: I would look into response transformation for future references see Eloquent Resources or Fractal. Instead of doing to much if logic, you can use FormRequest to validate the input.

ZF2AuthAcl Module doesnt work out of the box

I picked up this ZF2AuthAcl module to make my life easier. For some reason it does not work out of the box. As soon as i activate it in Zend2 Application.config it takes over the whole site. Meaning it goes straight to login on any page i have. There is a "white list" and i tried to add pages to this in an array and it does not seem to work. I will show the Acl page that it has with the "white list" maybe i did not add them correctly or there is a better way. It is data driven also. Has anyone used this with success or know about it?
The author is the one who told me it probably has to do with the white list.
The area that i added to looked like this:
public function initAcl()
{
$this->roles = $this->_getAllRoles();
$this->resources = $this->_getAllResources();
$this->rolePermission = $this->_getRolePermissions();
// we are not putting these resource & permission in table bcz it is
// common to all user
$this->commonPermission = array(
'ZF2AuthAcl\Controller\Index' => array(
'logout',
'index'
),
);
$this->_addRoles()
->_addResources()
->_addRoleResources();
}
This is the whole thing with parts i added.
namespace ZF2AuthAcl\Utility;
use Zend\Permissions\Acl\Acl as ZendAcl;
use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Acl extends ZendAcl implements ServiceLocatorAwareInterface
{
const DEFAULT_ROLE = 'guest';
protected $_roleTableObject;
protected $serviceLocator;
protected $roles;
protected $permissions;
protected $resources;
protected $rolePermission;
protected $commonPermission;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function initAcl()
{
$this->roles = $this->_getAllRoles();
$this->resources = $this->_getAllResources();
$this->rolePermission = $this->_getRolePermissions();
// we are not putting these resource & permission in table bcz it is
// common to all user
$this->commonPermission = array(
'ZF2AuthAcl\Controller\Index' => array(
'logout',
'index'
),
'Frontend\Controller\Index' => array(
'index'
),
'Blog\Controller\Blog' => array(
'blog',
'list',
'view',
'UsMap',
'maps'
)
);
$this->_addRoles()
->_addResources()
->_addRoleResources();
}
public function isAccessAllowed($role, $resource, $permission)
{
if (! $this->hasResource($resource)) {
return false;
}
if ($this->isAllowed($role, $resource, $permission)) {
return true;
}
return false;
}
protected function _addRoles()
{
$this->addRole(new Role(self::DEFAULT_ROLE));
if (! empty($this->roles)) {
foreach ($this->roles as $role) {
$roleName = $role['role_name'];
if (! $this->hasRole($roleName)) {
$this->addRole(new Role($roleName), self::DEFAULT_ROLE);
}
}
}
return $this;
}
protected function _addResources()
{
if (! empty($this->resources)) {
foreach ($this->resources as $resource) {
if (! $this->hasResource($resource['resource_name'])) {
$this->addResource(new Resource($resource['resource_name']));
}
}
}
// add common resources
if (! empty($this->commonPermission)) {
foreach ($this->commonPermission as $resource => $permissions) {
if (! $this->hasResource($resource)) {
$this->addResource(new Resource($resource));
}
}
}
return $this;
}
protected function _addRoleResources()
{
// allow common resource/permission to guest user
if (! empty($this->commonPermission)) {
foreach ($this->commonPermission as $resource => $permissions) {
foreach ($permissions as $permission) {
$this->allow(self::DEFAULT_ROLE, $resource, $permission);
}
}
}
if (! empty($this->rolePermission)) {
foreach ($this->rolePermission as $rolePermissions) {
$this->allow($rolePermissions['role_name'], $rolePermissions['resource_name'], $rolePermissions['permission_name']);
}
}
return $this;
}
protected function _getAllRoles()
{
$roleTable = $this->getServiceLocator()->get("RoleTable");
return $roleTable->getUserRoles();
}
protected function _getAllResources()
{
$resourceTable = $this->getServiceLocator()->get("ResourceTable");
return $resourceTable->getAllResources();
}
protected function _getRolePermissions()
{
$rolePermissionTable = $this->getServiceLocator()->get("RolePermissionTable");
return $rolePermissionTable->getRolePermissions();
}
private function debugAcl($role, $resource, $permission)
{
echo 'Role:-' . $role . '==>' . $resource . '\\' . $permission . '<br/>';
}
}
06/10/2016 Additional information
I have also found that this ACL page is not in any of the pages in the module. The functions are not called out anywhere in any page nor is it "use" on any page. So how is it supposed to work?
Update 06/10/2017 - Area that has been fixed.
I have found where this is used in the module.php there is a whitelist that the pages have to be added too. Below is where you add them.
$whiteList = array(
'Frontend\Controller\Index-index',
*Add whatever modules/controller/action you do not want included*
'ZF2AuthAcl\Controller\Index-index',
'ZF2AuthAcl\Controller\Index-logout'
);
Above is the conclusion of my issue. I stumbled upon it. I did not look in the module.php file. That is where the answer was.
Here is a general implementation of Zend ACL. I followed this one. If you wish you can follow this one too.
Create a file named module.acl.php in the config/ folder of your module. This file contains configuration for roles and permissions. Modify this script as you need.
ModuleName/config/module.acl.php
return array(
'roles' => array(
'guest',
'member'
),
'permissions' => array(
'guest' => array(
// Names of routes for guest role
'users-signup',
'users-login'
),
'member' => array(
// Names of routes for member role
// Add more here if you need
'users-logout'
)
)
);
You need to import the following three classes and define and initialize some methods in the Module.php.
ModuleName/Module.php
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole;
use Zend\Permissions\Acl\Resource\GenericResource;
// Optional; use this for authentication
use Zend\Authentication\AuthenticationService;
Now lets create methods that will deploy ACL and check roles and permissions.
Module::initAcl()
public function initAcl(MvcEvent $e)
{
// Set the ACL
if ($e->getViewModel()->acl == null) {
$acl = new Acl();
} else {
$acl = $e->getViewModel()->acl;
}
// Get the roles and permissions configuration
// You may fetch configuration from database instead.
$aclConfig = include __DIR__ . '/config/module.acl.php';
// Set roles
foreach ($aclConfig['roles'] as $role) {
if (!$acl->hasRole($role)) {
$role = new GenericRole($role);
$acl->addRole($role);
} else {
$role = $acl->getRole($role);
}
// Set resources
if (array_key_exists($role->getRoleId(), $aclConfig['permissions'])) {
foreach ($aclConfig['permissions'][$role->getRoleId()] as $resource) {
if (!$acl->hasResource($resource)) {
$acl->addResource(new GenericResource($resource));
}
// Add role to a specific resource
$acl->allow($role, $resource);
}
}
}
// Assign the fully prepared ACL object
$e->getViewModel()->acl = $acl;
}
Module::checkAcl()
public function checkAcl(MvcEvent $e) {
// Get the route
$route = $e->getRouteMatch()->getMatchedRouteName();
// Use this if you have authentication set
// Otherwise, take this off
$auth = new AuthenticationService();
// Set role as you need
$userRole = 'guest';
// Use this if you have authentication set
// Otherwise, take this off
if ($auth->hasIdentity()) {
$userRole = 'member';
$loggedInUser = $auth->getIdentity();
$e->getViewModel()->loggedInUser = $loggedInUser;
}
// Check if the resource has right permission
if (!$e->getViewModel()->acl->isAllowed($userRole, $route)) {
$response = $e->getResponse();
// Redirect to specific route
$response->getHeaders()->addHeaderLine('Location', $e->getRequest()->getBaseUrl() . '/404');
$response->setStatusCode(404);
return;
}
}
Now call those above methods on the onBootstrap() method in your Module.php. Initialize Module::initAcl() and check resource permission by adding Module::checkAcl() to the route event.
Module::onBootstrap()
public function onBootstrap(MvcEvent $e)
{
$this->initAcl($e);
$e->getApplication()->getEventManager()->attach('route', array($this, 'checkAcl'));
}
Let us know it helps you or not!

Import Excel in Yii2

I have yii2 application using advanced template and database mySql, i already make function for import an excel file to one of the table,
I made the function in a controller named student that contains CRUD of students data.this is my code
public function actionImportExcel()
{
$inputFile = 'uploads/siswa_file.xlsx';
try{
$inputFileType = \PHPExcel_IOFactory::identify($inputFile);
$objReader = \PHPExcel_IOFactory::createReader($inputFileType);
$objPHPExcel = $objReader->load($inputFile);
} catch (Exception $e) {
die('Error');
}
$sheet = $objPHPExcel->getSheet(0);
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
for($row=1; $row <= $highestRow; $row++)
{
$rowData = $sheet->rangeToArray('A'.$row.':'.$highestColumn.$row,NULL,TRUE,FALSE);
if($row==1)
{
continue;
}
$siswa = new Siswa();
$siswa->nis = $rowData[0][0];
$siswa->nama_siswa = $rowData[0][1];
$siswa->jenis_kelamin = $rowData[0][2];
$siswa->ttl = $rowData[0][3];
$siswa->alamat = $rowData[0][4];
$siswa->telp = $rowData[0][5];
$siswa->agama = $rowData[0][6];
$siswa->nama_ortu = $rowData[0][7];
$siswa->telp_ortu = $rowData[0][8];
$siswa->pekerjaan_ortu = $rowData[0][9];
$siswa->tahun_masuk = $rowData[0][10];
$siswa->kelas = $rowData[0][11];
$siswa->save();
print_r($siswa->getErrors());
}
die('okay');
}
but i don't know how to make a button in a view to make this function work. i mean i want to make a button that when the user click the button and browse their excel file they can import that file and the data inside the excel can import to database
First you should upload the file
and then processing with your function
there are several parts of code you must produce ..
eg a view for the user to upload the file
View: #app/views/site/upload.php
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
<?= $form->errorSummary($model); ?>
<?= $form->field($model, 'imageFile')->fileInput() ?>
<button>Submit</button>
<?php ActiveForm::end() ?>
Controller: #app/controllers/SiteController.php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\UploadForm;
use yii\web\UploadedFile;
class SiteController extends Controller
{
public function actionUpload()
{
$model = new UploadForm();
if (Yii::$app->request->isPost) {
$model->imageFile = UploadedFile::getInstance($model, 'imageFile');
if ($model->upload()) {
// file is uploaded successfully
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}
Model: #app/models/UploadForm.php
namespace app\models;
use yii\base\Model;
use yii\web\UploadedFile;
class UploadForm extends Model
{
/**
* #var UploadedFile
*/
public $imageFile;
public function rules()
{
return [
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload()
{
if ($this->validate()) {
$this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);
return true;
} else {
return false;
}
}
}
the code is from this doc

How to access configs from autoloaded config files in a layout / view script in Zend Framework 2?

I would like / have to manage some settings in ZF1 style and provide the view with the infomation about the current environment.
/config/application.config.php
return array(
...
'module_listener_options' => array(
...
'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php')
)
);
/config/autoload/env.local.php
return array(
// allowed values: development, staging, live
'environment' => 'development'
);
In a common view script I can do it over the controller, since the controllers have access to the Service Manager and so to all configs I need:
class MyController extends AbstractActionController {
public function myAction() {
return new ViewModel(array(
'currentEnvironment' => $this->getServiceLocator()->get('Config')['environment'],
));
}
}
Is it also possible to get the configs in a common view directly?
How can I access the configs in a layout view script (/module/Application/view/layout/layout.phtml)?
(My implementation/interpretation of) Crisp's suggestion:
Config view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class Config extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
public function __invoke() {
$config = $this->serviceManager->getServiceLocator()->get('Config');
return $config;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'config' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\Config($serviceManager);
return $helper;
},
)
);
}
Layout view script
// do whatever you want with $this->config()['environment'], e.g.
<?php
if ($this->config()['environment'] == 'live') {
echo $this->partial('partials/partial-foo.phtml');;
}
?>
My implementation/interpretation of Sam's solution hint for the concret goal (to permit displaying the web analytics JS within the dev/staging environment):
DisplayAnalytics view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class DisplayAnalytics extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
public function __invoke() {
if ($this->serviceManager->getServiceLocator()->get('Config')['environment'] == 'development') {
$return = $this->view->render('partials/partial-bar.phtml');
}
return $return;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'displayAnalytics' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\DisplayAnalytics($serviceManager);
return $helper;
}
)
);
}
Layout view script
<?php echo $this->displayAnalytics(); ?>
So this solution becomes more flexible:
/config/application.config.php
return array(
...
'module_listener_options' => array(
...
'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php')
)
);
/config/autoload/whatever.local.php and /config/autoload/whatever.global.php
return array(
// allowed values: development, staging, live
'environment' => 'development'
);
ContentForEnvironment view helper
<?php
namespace MyNamespace\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\HelperPluginManager as ServiceManager;
class ContentForEnvironment extends AbstractHelper {
protected $serviceManager;
public function __construct(ServiceManager $serviceManager) {
$this->serviceManager = $serviceManager;
}
/**
* Returns rendered partial $partial,
* IF the current environment IS IN $whiteList AND NOT IN $blackList,
* ELSE NULL.
* Usage examples:
* Partial for every environment (equivalent to echo $this->view->render($partial)):
* echo $this->contentForEnvironment('path/to/partial.phtml');
* Partial for 'live' environment only:
* echo $this->contentForEnvironment('path/to/partial.phtml', ['live']);
* Partial for every environment except 'development':
* echo $this->contentForEnvironment('path/to/partial.phtml', [], ['development', 'staging']);
* #param string $partial
* #param array $whiteList
* #param array $blackList optional
* #return string rendered partial $partial or NULL.
*/
public function __invoke($partial, array $whiteList, array $blackList = []) {
$currentEnvironment = $this->serviceManager->getServiceLocator()->get('Config')['environment'];
$content = null;
if (!empty($whiteList)) {
$content = in_array($currentEnvironment, $whiteList) && !in_array($currentEnvironment, $blackList)
? $this->view->render($partial)
: null
;
} else {
$content = !in_array($currentEnvironment, $blackList)
? $this->view->render($partial)
: null
;
}
return $content;
}
}
Application Module class
public function getViewHelperConfig() {
return array(
'factories' => array(
'contentForEnvironment' => function($serviceManager) {
$helper = new \MyNamespace\View\Helper\ContentForEnvironment($serviceManager);
return $helper;
}
)
);
}
Layout view script
<?php echo $this->contentForEnvironment('path/to/partial.phtml', [], ['development', 'staging']); ?>