Slim framework - Why does not halt method work if the controller extend the Slim class? - response

I have seen a very interesting thing. I did a little mvc/rest framework based on the Slim framework.
$app->put('/:id', function ($id) {
$app->halt(500, "Error") // Here this is working.
(new RestController)->editItem($id);
})->via('put');
So I wrote a RestController which extends the BaseController, and my BaseController extends the Slim framework.
class BaseController extends \Slim\Slim {
/**
* #var \app\models\AbstractModel
*/
protected $model;
public function __construct() {
$settings = require(__DIR__ .'/../configurations/slim.php');
parent::__construct($settings);
}
}
So my BaseController can uses the Slim class's methods and properties.
class RestController extends BaseController {
public function editItem($id) {
$data = $this->getRequestBody();
$result = $this->model->update($id, $data['data']);
// This is absolutely not working, but it seems my application will die in this case.
// Because I cannot see any written message (with echo or print methods...)
// This will always return with a 200 staus code and blank page!
$this->halt(404, json_encode(array('status' => "ERROR")));
}
}
But this will work fine... and I do not get it, why?
class RestController extends BaseController {
public function editItem($id) {
$data = $this->getRequestBody();
$result = $this->model->update($id, $data['data']);
// This will work.
$app = Slim::getInstance();
$app->halt(204, json_encode(array('status' => "ERROR")));
}
}
Anyone has a good ide?

You construct a new RestController in your route by doing (new RestController). Although it extends Slim/Slim in the class hierarchy, it is a new instance of it; it is not a copy of or reference to $app, the Slim application that is actually being run.
This causes issues in the first case: you say $this->halt(...) while $this is the newly constructed RestController which is a new instance of a Slim app, not the one that is currently running. Therefore the halt call has no effect on your the output of your application.
In the second case, you say $app->halt(...) where $app is Slim::getInstance(), the global instance of your Slim application, which is the actual running Slim app. Therefore the halt call has effect on the output of your application.
You can solve the issue either by using the second approach, or by instantiating your global $app variable as a RestController, and then you can do:
$app->put('/:id', function ($id) use ($app) {
$app->halt(500, "Error") // Here this is working.
$app->editItem($id);
})->via('put');
NB; you forgot to put use ($app) in the code posted to your question, I added it in my code above. Is it present in the code you are actually running? Otherwise the $app->halt() call should result in an error in any case.

Related

How do I create a "getter" for my modules (How do you instantiate a Module Object using a Navigator?)

I am trying to make it so that all of my page and module references can autocomplete in intellij.
Due to some sort of bug I am unable to do this like one normally would. (see here for more details: How to have geb static content recognized form test script )
In order to work around the above mentioned bug. I opted to create "getters" for all of my static content.
for example:
The Page:
class MyPage extends Page{
static content = {
tab {$(By.xpath("somexpath")}
}
Navigator tab(){
return tab
}
}
The Script:
//imagine we are in the middle of a feature method here
def test = at MyPage
test.tab().click()
So all of the above code works as I expect it to, and I want to redo my pages like this so that I can have autocomplete from the script side. Problems occur when I try to use this same technique for modules.
For example:
class MyPage extends Page{
static content = {
mod {module(new MyModule())}
}
MyModule mod(){
return mod
}
}
If I try and access mod from the script like so
//imagine we are in the middle of a feature method here
def test = at MyPage
test.mod().someModContentMaybe().click()
I get the following error:
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'MyPage' -> mod: 'MyModule' with class 'geb.content.TemplateDerivedPageContent' to class 'MyModule'
If I try to do the following in the page object:
class MyPage extends Page{
static content = {
mod {module(new MyModule())}
}
MyModule mod(){
return new MyModule()
}
}
I get the following error when attempting to access the module from the script:
geb.error.ModuleInstanceNotInitializedException: Instance of module class MyModule has not been initialized. Please pass it to Navigable.module() or Navigator.module() before using it.
I guess it wants me to take an instantiated Navigator Object and and to call module(MyModule) but I am not sure how this works or how one would decide which Navigator Object to call module from.
All in all, I just want to be able to autocomplete module Names and static content from my scripts.
The Book of Geb's section about modules answers your question. You should not manually call the module's constructor, but instead instead use the syntax described right at the beginning of the chapter. This solution gets rid of the exception and also solves the code completion problem for me:
static content = {
mod { module MyModule }
}
Now that the exception is gone here is how to add the getter you asked for:
def myModule() { mod }
You're getting a GroovyCastException when returning content that contains a module from a method whose return type is a class which extends geb.Module because navigators and modules returned from content definitions get wrapped in geb.content.TemplateDerivedPageContent.
You can unwrap them using the as keyword as explained in the manual section about unwrapping modules returned from the content DSL. So, for one of your examples it would look like this:
MyModule mod(){
mod as MyModule
}
I think the problem is you content block. Modules are defined via Navigators' module method:
static content = {
mod { $("div.module").module(MyModule)
}
So no constructor calling required.

How do you call a Service outside of a Symfony3 controller? And queries from Repository

I have two questions today. This is detailed because too many other replies rely on assumptions and have not been detailed enough. I hope that this is detailed and will be able to help lots of developers.
1st. The code below points to the real question I have. How do you call a Service outside of the controller since the $this->get() method is inside of the controller only? This is not in any of the documentation or on KNP University's tutorial on Services.
2nd. From what I have read, according to some, not all, if you call to a Repository, from anywhere, it should automatically instantiate the Entity Repository. I don't think this is so. Tell me if I am right or wrong.
See the following below....
My Default Controller, it's straightforward call a class and let it do some work. As an example, I called it with a Service and a conventional OO method:
<?php
// src/AppBundle/Controller/DefaultController.php
// Here is where I am starting. There is a service
// and there is a conventional OO call.
// Both should invoke the same thing.
namespace AppBundle\Controller;
use AppBundle\Service;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
// Step 1.... Do a little of this.
// Step 2.... Do some of that.
// Step 3.... Call another class to do some logic and it will
// eventually call a query...
// Invoking my service
$obj_via_service = $this->get('app.services.process_question');
$result1 = $obj_via_service->submitQuestion();
// Invoking via Namespace and Call
$obj_via_new = new Service\ProcessQuestion();
$result2 = $obj_via_new->submitQuestion();
dump($result1);
dump($result2);
die();
}
}
My Service.yml File.
# src/app/config/services.yml
parameters:
services:
app.services.process_question:
class: AppBundle\Service\ProcessQuestion
app.rep.geo_state:
class: AppBundle\Entity\GeoStateRepository
arguments: ['#doctrine.orm.entity_manager']
This is my class that is doing the work for me. I want to be able to call the second service ^^above^^ but I can't because I can't use $this->get() outside of the controller.
<?php
// src/AppBundle/Service/ProcessQuestion.php
namespace AppBundle\Service;
class ProcessQuestion
{
public function submitQuestion()
{
// Step 1.... Do this.
// Step 2.... Do that.
// Step 3.... Query for some data...
// Invoke my repository class via a Service Call....
// but I cannot do that because 'get' is a part of the
// controller...
$obj_via_service = $this->get('app.rep.geo_state');
**^^ ^^**
**^^ This is what won't work ^^**
$results = $obj_via_service->selectStates();
return $results;
}
}
My Repository Class... Keep in mind I cannot reach this class yet, but I am throwing it in here so that other new Symfony 3 developers can see this.
<?php
// src/AppBundle/Repository/GeoState.php
// My Repository Class where I want to do some queries...
namespace AppBundle\Repository;
use Doctrine\ORM\EntityRepository;
class GeoStateRepository extends EntityRepository
{
/**
* #Mapping\Column(type="string")
*/
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function selectStates()
{
$sql = "SELECT * FROM geo_state";
return $this->getEntityManager()->createQuery($sql)->getResult();
}
}
Why is this so hard to find an example? Also, I have followed a bunch of the Symfony 2.x documentation and the nuances are hard to port into Symfony 3 sometimes.
I think Fabian re purposed too much of the docs for 2.x to go into 3.x and there is not any good examples on coding that is between the New Developer level and the Hard Core Developer level. If you are at Sensio and reading this, please keep in mind that there is a middle ground we need to cover and most of the screencasts that out there and much of the better documentation is not in English.
You should really read more about Dependency Injection.
Symfony is very good at this .
Regarding your question about using app.rep.geo_state service in the app.services.process_question service .
In Symfony/ DI terminology it's can be termed as injecting a service into another service .
The documentation on how to do this is very good.
this is how it can be done.
services:
app.services.process_question:
class: AppBundle\Service\ProcessQuestion
arguments: ['#app.rep.geo_state']
app.rep.geo_state:
class: AppBundle\Entity\GeoStateRepository
arguments: ['#doctrine.orm.entity_manager']
And in the class
<?php
// src/AppBundle/Service/ProcessQuestion.php
namespace AppBundle\Service;
use AppBundle\Entity\GeoStateRepository;
class ProcessQuestion
{
private $geoRepository;
public function __construct(GeoStateRepository $geoRepository)
{
$this->geoRepository = $geoRepository;
}
public function submitQuestion()
{
//now you can call $this->geoRepository
}
}
Also note that $this->get() is only a shortcut function provided by the Symfony base Controller class to access the container.
To know more about DI, you can read Fabian's excellent articles about this in his blog .

Creating notifications with Zend Framework 2

I am trying to learn ZF2. I know that there are better methods to create notifications but as I said this is for learning purpose.
My idea is to save the notifications in the database. This is simple I can do it. My question is about how to show them. I have a partial for the header menu , I want to display them there.
I dont know if i am on the right track i currently have a view helper which i create via factory.
class TestMe extends AbstractHelper
{
protected $html;
public function __invoke($name = 'Unnamed')
{
// if($this->view->hasIdentity()){
// $user = $this->view->identity();
// }
//$this->testJS();
return "$name , this is Zend Framework 2 View Helper";
}
protected function htmlIt(){
}
protected function testJS($loggedIn = false){
$js = '';
$js .= <<<JS
alert('test');
JS;
$view = $this->getView();
$view->inlineScript()->prependScript($js);
}
}
I know this is nothing but I cant understand what is good an wrong. My idea is to pass ot the view helper an array with notifications for the user and display them. So I need a service for making calls to the database or there is another way?
ZF2 already has an Notification Helper Plugin. Have a Look at http://framework.zend.com/manual/current/en/modules/zend.view.helpers.flash-messenger.html
But if you really want to do it on your own, you may want to create an Controller Plugin http://framework.zend.com/manual/current/en/modules/zend.mvc.plugins.html.
There is a nice Tutorial for that here: http://lab.empirio.no/custom-controller-plugin-in-zf2.html

Inject ServiceLocator in Validator

How can I inject the "normal" ServiceManger into a custom validator used for REST calls (Use without Form). ZF 2.2.7 Used to inject an instance of external library into an validator.
I have tried the following, and nothing works:
Inject it with the ValidationPluginManager, service not found
Inject it via factory, factory will not be loaded in validator chain
Inject it via validator options, not possible because the "ServiceManager" is an instance of ValidationPluginManager with the asme result as mentioned in #1
Is there any concept how to solve this problem, or do i have to give up and link all libraries statically?
Not tested this and have never done with with ValidationPluginManager but works with ControllerManager, FormElementManager etc
// GetServiceLocator call should return Instance of ServiceManager
// Then retrieve the service, Yay!
$validationPluginManager->getServiceLocator()->get('SomeService')
There has been a discussion on github about a somewhat similar problem here. They suggested to use Zend\Form\FormAbstractServiceFactory and tinker with dependencies there (weierophinney before closing the topic).
In your post you mention you are not using a form did you mean you are not using the form in a classic kind of way or are you bypassing the whole form in particular?
It simply seems off to me to use a validator if there isn't a form present. Could you elaborate more on that?
EDIT: To my understanding zf2 requires that your input filters have form elements like 'inputs' etc. You did not post any code and I simply do not know if/or your able to bypass this somehow. I still do not understand why you'd still want to use validators in combination of input filters. I would simply skip the input filters and write the custom validator.
My personal preferences is to write factories instead of anonymous functions within module.php files. But this also could work the anonymous function way.
I would then simply resolve the dependencies within the customValidatorFactory and get the factory within my controller or whatever place I would need it.
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use CustomValidator;
class CustomValidatorFactory implements FactoryInterface
{
/**
* Create Service Factory
*
* #param ServiceLocatorInterface $serviceLocator
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$sm = $serviceLocator->getServiceLocator();
$customService = $sm->get('Application\Service\Geocoding');
$validator= new CustomValidator();
$validator->setCustomService($service);
return $validator;
}
}
// CustomValidator.php
class CustomValidator extends Zend\Validator\AbstractValidator
{
public function setCustomService($service)
{
$this->service = $service;
}
public function isValid($value)
{
$customService = $this->service;
if ($customService->customMethod() == true) {
return true;
}
return false;
}
}
//module-config.php
'service_manager' => array(
'factories' => array(
'custom\ValidatorFactory' => 'Namespace\To\CustomValidatorFactory',
),
),
//yourController or whatever.php will require access to the service manager
$customValidation = $sm->get('custom\ValidatorFactory');
// should return true or false now
$state = $customValidation->isValid($someValue);

Wicket and Reusable Link

'm trying to create a reusable link class that extends Link. I have a webpage with about 7 menu items and I'm using inheritance for my application. I want to create a reusable link class to shorten the length of my code..
As of now the link creates and runs fine when I add(new Link....) as an anonymous class inside oninitialize().
The custom link class (which is an inner class of the base page) works fine when I hard code the instance of the new page to go to, and assign it to a "Page" reference, then pass it into setResponsePage();
The problem is, I'm passing trying to be able to pass object through the constructor generically. When I pass it through the constructor, and try to travel to the new page, I get a session has expired.
I've tried using generics for the class, and I've also tried just declaring a Page reference as a parameter value. Am I supposed to use some sort of Model? Or can someone provide an example of how to do this? I want to be able to use this custom link class to add new links for the 7 menu items, which each have there own class...
Code that works:
add(new Link("userPageLink")
{
public void onClick()
{
pageTitle = "User";
Page next = new UserPage();
setResponsePage(next);
}
});
Modified code that gives page expired upon click:
public class CustomLinkToNewPage extends Link
{
private String title;
private Page next;
public CustomLinkToNewPage(String id, String title, Page newPage)
{
super(id);
next = newPage;
this.title = title;
}
#Override
public void onClick()
{
SSAPage.pageTitle = title;
setResponsePage(next);
}
}
This might be due to the fact that in the first version you crate the Page object when the onClick method of the Link object is called and in the second version, the Page object is created on Page-construction (way earlier).
You might get the result if you pass the Pageclass of the responsepage instead on an instance.
Component features setters for these either with
public final <C extends IRequestablePage> void setResponsePage(java.lang.Class<C> cls, PageParameters parameters)
or without parameters.
public final <C extends IRequestablePage> void setResponsePage(java.lang.Class<C> cls)
See Javadoc for more information.
I ended up doing:
public class CustomLinkToNewPage<T extends SSAPage> extends Link
SSAPage is my base page that extends WebPage... So any object passed in to this class's constructor must extend SSAPage as well.
public CustomLinkToNewPage(String id, Class<T> name)
Then I passed in the .class reference to the object, and created a new instance of the object using reflection.. then set that instance to Page, and passed it to setResponsePage in my onClick. Worked nicely, as I couldn't figure out how to do Nicktar's way. So this an alternative in case anyone else runs into this issue.

Resources