Get route params in RESTful controller - rest

In my RestController which extends AbstractRestfulController, I can get the route params in the implemented functions such as...
public function create($data)
{
$entity = $this->params()->fromRoute('entity');
}
... but when I do the same in the constructor like this
public function __construct()
{
$entity = $this->params()->fromRoute('entity');
}
I get Call to a member function getParam() on a non-object.
Why is that? How can I get the route parameters in the constructor?
What I am trying to do
Since I'm trying to create a generic controller, there is a part of the restful route that is shared for all actions (resp. verbs). The entity for which the request is made. I'd like to store this in a class parameter for convenience.

Normally you'd write a method to proxy to whatever value you need, and just call that method, it's only a little more expensive to call $this->getEntity() than it is to call $this->entity, which, as far as I can tell is the stated aim
class RestController
{
protected $entity;
public function getEntity()
{
if (!$this->entity) {
$this->entity = $this->params()->fromRoute('entity');
}
return $this->entity;
}
}
If you really do want to pre-populate the entity property, the simplest method is to use an initializer, and move the code from your __constructor to init(). Have your controller implement \Zend\Stdlib\InitializableInterface
use Zend\Stdlib\InitializableInterface;
class RestController extends AbstractRestfulController implements InitializableInterface
{
protected $entity;
public function init() {
$this->entity = $this->params()->fromRoute('entity');
}
}
Add an initializer to the controller loader in your module boostrap
use Zend\Stdlib\InitializableInterface;
class Module
{
public function onBootstrap(MvcEvent $e)
$sm = $e->getApplication()->getServiceManager();
$controllers = $sm->get('ControllerLoader');
$controllers->addInitializer(function($controller, $cl) {
if ($controller instanceof InitializableInterface) {
$controller->init();
}
}, false); // false tells the loader to run this initializer after all others
}
}

That would not make any sense as the route is matched to a particular action.
You can't route to a constructor, therefore how could you get route parameters there?
If you give an idea of what you are trying to do then I could suggest a better/nicer way to do it

Related

I can't pass parameters to the component Wire Elements Modal

I can't pass parameters to the component.
I have 3 files:
Inside Livewire/test.blade.php
...............
onclick='Livewire.emit("openModal", "test-modal", {{ json_encode(["id_code" => $client->id_code]) }})'>
Inside /Http/Livewire/TestModal.php
namespace App\Http\Livewire;
use LivewireUI\Modal\ModalComponent;
use App\Models\Client;
class TestModal extends ModalComponent
{
public $id_code;
public function render($id_code)
{
dd($id_code);
return view('livewire.test-modal');
}
}
And livewire.test-modal which displays the content of the modal window.
But I can't get the id_code.
Let's see if someone can help me with this. Thanks.
So I had the same issue pretty much.
I solved it by adding the $id_code to the mount method. I hope it helps
namespace App\Http\Livewire;
use LivewireUI\Modal\ModalComponent;
use App\Models\Client;
class TestModal extends ModalComponent
{
public $id_code;
public function mount($id_code){
$this->id_code = $id_code
}
public function render()
{
$elCliente = Client::where("erp_code", $this->id_code)->first();
dd($elCliente);
return view('livewire.test-modal');
}
}
Livewire.emit("openModal") will emit an event that you can listen to in your components. The render() method in Livewire does not accept a parameter, so instead you need to listen to that event, and do your actions in a separate method instead.
By adding
protected $listeners = ['openModal' => 'openModal'];
the component will now listen to the event openModal (key in the array) being dispatched, and then fire the method openModal() (value in the array). Since you pass in two parameters, "test-modal" and a JSON parameter, you can accept those in that method.
namespace App\Http\Livewire;
use LivewireUI\Modal\ModalComponent;
use App\Models\Client;
class TestModal extends ModalComponent
{
public $id_code;
protected $listeners = ['openModal' => 'openModal'];
public function openModal($name, $data)
{
$this->id_code = $data['id_code'];
}
public function render()
{
return view('livewire.test-modal');
}
}

Symfony3: Service not able to get arguments

I have made a service to get Doctrine connection in my models (Not sure if it is a nice approach but I dont want to pass connection from controller to model constructor each time).
So lets say I want products in my controller
public function getProductsAction(Request $request) {
$product_model = new ProductModel();
return $product_model->getProducts();
}
I have Product model Which will access a helper to get "database_connection"
use AppBundle\Helper\ContainerHelper;
class ProductModel {
function getProducts() {
$helper = new ContainerHelper();
$db = $helper->getDoctrine();
$query = "SELECT * FROM customer_products;";
$statement = $db->prepare($query);
$statement->execute();
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
}
Now this helper is defined in src/AppBundle/Helper/ContainerHelper.php
namespace AppBundle\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
class ContainerHelper {
private $container;
public function __construct(Container $container) {
$this->container = $container;
}
public static function getDoctrine() {
$database_connection = $this->container->get('database_connection');
return $database_connection;
}
}
Lets say this service needs "service container" so in app/config/services.yml
services:
app.container_helper:
class: AppBundle\Helper\ContainerHelper
arguments: ['#service_container']
But it gives me error:
Catchable Fatal Error: Argument 1 passed to
AppBundle\Helper\ContainerHelper::__construct() must implement
interface Symfony\Component\DependencyInjection\ContainerInterface,
none given, called in \src\AppBundle\Model\ProductModel.php
on line 148 and defined
While I believe that I have implemented it correctly according to http://symfony.com/doc/current/book/service_container.html and http://anjanasilva.com/blog/injecting-services-in-symfony-2/, its certain that I have missed something or just got the whole bad idea. I need to know if it is a correct concept or what I have missed
While #pavlovich is trying to fix your existing code, I really think you are making this much more convoluted than it has to be. ProductModel itself should be a service with your database connection injected into it.
class ProductModel {
public function __construct($conn) {
$this->conn = $conn;
}
public function getProducts() {
$stmt = $this->conn->executeQuery('SELECT * FROM customer_products');
return $stmt->fetchAll();
}
services:
product_model:
class: AppBundle\...\ProductModel
arguments: ['#database_connection']
// controller.php
$productModel = $this->get('product_model'); // Pull from container
$products = $productModel->getProducts();
Rather than using helpers, I'd recommend using constructor injection and autowiring. It's more safe, future proof and easier to extend and test.
In such case, you'd have to create ProductRepository (more common and standard name for ProductModel) and pass it to controller.
1. Controller
<?php
class SomeController
{
/**
* #var ProductRepository
*/
private $productRepository;
public function __construct(ProductRepository $productRepository)
{
$this->productRepository = $productRepository;
}
public function getProductsAction()
{
return $this->productRepository->getProducts();
}
}
If you have difficulties to register controller as a service, just use Symplify\ControllerAutowire bundle.
2. ProductRepository
// src/AppBundle/Repository/ProductRepository.php
namespace AppBundle\Repository;
class ProductRepository
{
/**
* #var Doctrine\DBAL\Connection
*/
private $connection;
public function __construct(Doctrine\DBAL\Connection $connection)
{
$this->connection = $connection;
}
public function fetchAll()
{
$query = "SELECT * FROM customer_products;";
$statement = $this->connection->prepare($query);
$statement->execute();
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
}
3. Service registration
# app/cofig/servies.yml
services:
product_repository:
class: AppBundle\Repository\ProductRepository
autowire: true
For more you can see similar question with answer here: Symfony 3 - Outsourcing Controller Code into Service Layer
With new version of Symfony 3.3, a new feature is added (Auto-wired Services Dependencies)
https://symfony.com/doc/current/service_container/autowiring.html
https://symfony.com/doc/current/service_container/3.3-di-changes.html
Using this feature, I solved this issue in following way:
Added a new directory /src/AppBundle/Model
Added my model classes in this directory
namespace AppBundle\Modal;
use Doctrine\ORM\EntityManagerInterface;
class ProductModal
{
private $em;
// We need to inject this variables later.
public function __construct(EntityManagerInterface $entityManager)
{
$this->em = $entityManager;
}
// We need to inject this variables later.
public function getProducts()
{
$statement = $this->em->getConnection()->prepare("SELECT * FROM product WHERE 1");
$statement->execute();
$results = $statement->fetchAll();
return $results;
}
}
Added in my app/config/services.yml
AppBundle\Modal\:
resource: '../../src/AppBundle/Modal/*'
public: true
In my controller I can use it like
$products = $this->get(ProductModal::class)->getProducts();
P.S. Dont forget to add use AppBundle\Entity\Product\Product; in controller

How to call a method of another controller in zend?

I have a one controller SearchController in Hotel module and i have a one method Search in this controller like below.
public function searchAction()
{
// Code for search
// want to call getlatlng method
}
now i created a one new controller DistanceController in same module and
created a one getlatlng method in distance controller.my getlatlng method like this.
public function getlatlng()
{
$location = $_REQUEST['lat'].','.$_REQUEST['lng'];
return $location;
}
Now i want to call a getlatlng method in searchAction method. which returns a latitude and longitude of the current place.I pass latitude and logitude to getlatlng function using post or get.
So how can i call getlatlng method in searchAction method?
You can call like this, If you are using ZF Version 1.x:
class SearchController extends Zend_Controller_Action
{
public function searchAction() {
echo "search_action_from_SearchController";
require_once ('DistanceController.php');
$distanceCtrl = new DistanceController($this->_request, $this->_response);
$distanceCtrl->xyzAction();
die;
}
}
class DistanceController extends Zend_Controller_Action
{
public function getlatlng() {
echo "getlatlng_from_DistanceController";
die;
}
}
Output:
URL: http://www.example.com/search/search
search_action_from_SearchController
getlatlng_from_DistanceController

Print data received by REST call when using #Resource in Grails

Following along with groovies docs on REST, i've setup a model like so:
import grails.rest.*
#Resource(uri='/books')
class Book {
String title
static constraints = {
title blank:false
}
}
I'd print out the parameters I receive when creating and saving. Is there away to override these methods created by the #Resource(uri='/books') annotation? Or handle the annotation a closure or something to do this?
I think you may have 2 choices if you wish to have a default RESTful interface and modify it somewhat for your needs.
Use the $ grails generate-controller [Domain Class Name] command that will generate the appropriate controller and change the generated file as needed.
Create a Book controller and extend the RestfulController; then override the default methods with the #Override annotation, print/log the params, and then call the matching super method.
import grails.rest.RestfulController
class BookController extends RestfulController {
static responseFormats = ['json', 'xml']
BookController() {
super(Book)
}
#Override
def save() {
println params
super.save params
}
#Override
def update() {
println params
super.update params
}
}

How do I mock Class<? extends List> myVar in Mockito?

I want to mock a Class in Mockito. It will then have a .newInstance() call issued which will be expected to return an actual class instance (and will return a mock in my case).
If it was setup correctly then I could do:
ArrayList myListMock = mock(ArrayList.class);
when(myVar.newInstance()).thenReturn(myListMock);
I know I can set it up so that a new instance of class ArrayList will be a mock (using PowerMockito whenNew), just wondering if there was a way to mock this kind of a class object so I don't have to override instance creation...
Below is the real class I'm trying to mock, I can't change the structure it is defined by the interface. What I'm looking for is a way to provide cvs when initialize is called.
public class InputConstraintValidator
implements ConstraintValidator<InputValidation, StringWrapper> {
Class<? extends SafeString> cvs;
public void initialize(InputValidation constraintAnnotation) {
cvs = constraintAnnotation.inputValidator();
}
public boolean isValid(StringWrapper value,
ConstraintValidatorContext context) {
SafeString instance;
try {
instance = cvs.newInstance();
} catch (InstantiationException e) {
return false;
} catch (IllegalAccessException e) {
return false;
}
}
Mockito is designed exclusively for mocking instances of objects. Under the hood, the mock method actually creates a proxy that receives calls to all non-final methods, and logs and stubs those calls as needed. There's no good way to use Mockito to replace a function on the Class object itself. This leaves you with a few options:
I don't have experience with PowerMock but it seems it's designed for mocking static methods.
In dependency-injection style, make your static factory method into a factory instance. Since it looks like you're not actually working with ArrayList, let's say your class is FooBar instead:
class FooBar {
static class Factory {
static FooBar instance;
FooBar getInstance() {
if (instance == null) {
instance = new FooBar();
}
return instance;
}
}
// ...
}
Now your class user can receive a new FooBar.Factory() parameter, which creates your real FooBar in singleton style (hopefully better and more threadsafe than my simple implementation), and you can use pure Mockito to mock the Factory. If this looks like it's a lot of boilerplate, it's because it is, but if you are thinking of switching to a DI solution like Guice you can cut down a lot of it.
Consider making a field or method package-private or protected and documenting that it's visible for testing purposes. Then you can insert a mocked instance in test code only.
public class InputConstraintValidator implements
ConstraintValidator<InputValidation, StringWrapper> {
Class<? extends SafeString> cvs;
public void initialize(InputValidation constraintAnnotation) {
cvs = constraintAnnotation.inputValidator();
}
public boolean isValid(StringWrapper value,
ConstraintValidatorContext context) {
SafeString instance;
try {
instance = getCvsInstance();
} catch (InstantiationException e) {
return false;
} catch (IllegalAccessException e) {
return false;
}
}
#VisibleForTesting protected getCvsInstance()
throws InstantiationException, IllegalAccessException {
return cvs.newInstance();
}
}
public class InputConstaintValidatorTest {
#Test public void testWithMockCvs() {
final SafeString cvs = mock(SafeString.class);
InputConstraintValidator validator = new InputConstraintValidator() {
#Override protected getCvsInstance() {
return cvs;
}
}
// test
}
}
I think you just need to introduce an additional mock for Class:
ArrayList<?> myListMock = mock(ArrayList.class);
Class<ArrayList> clazz = mock(Class.class);
when(clazz.newInstance()).thenReturn(myListMock);
Of course the trick is making sure your mocked clazz.newInstance() doesn't end up getting called all over the place because due to type-erasure you can't specify that it's actually a Class<ArrayList>.
Also, be careful defining your own mock for something as fundamental as ArrayList - generally I'd use a "real one" and populate it with mocks.