Here is the code in my unit test...
public static function member_put($f3,$args) {
// Id is for member: locohost
$f3->mock('PUT /member/c4774904-f15f-11e2-b7e4-00ffe024bd0b', array(
'firstname' => 'Not-Mark',
'lastname' => 'Not-Deibert'
));
}
Here is the Member model put method being called...
public static function put($f3,$args) {
self::validateArgs($args);
self::validatePost();
self::findById($args['id']);
self::$member->copyFrom('POST');
//var_dump(self::$member);
self::$member->save();
self::returnModel();
}
The Member put method is being called as expected, however the Member is not getting the new name fields from copyFrom('POST'). The var_dump still shows old values in the name fields. What am I doing wrong?
$_POST is available for POST method only.
For other methods (PUT, PATCH, DELETE) but also POST and GET, the http request body is stored in the BODY variable.
Therefore your put() function should look like:
public static function put($f3,$args) {
self::validateArgs($args);
self::validatePost();
self::findById($args['id']);
parse_str($f3->get('BODY'),$input);
$f3->set('INPUT',$input);
self::$member->copyFrom('INPUT');
//var_dump(self::$member);
self::$member->save();
self::returnModel();
}
Note that the request body is in the form of a query string: firstname=Not-Mark&lastname=Not-Deibert. That explains why it needs to be parsed with parse_str.
Related
I'm new at phpspec (coming from phpunit) and I have problems setting the behavior of a mock returned by another mock.
I'm creating a wrapper class around the Guzzle client and I want to check the output of the response.
Here's the spec:
function it_gets_response_status_code(Client $client, Url $url, Response $response)
{
$this->beConstructedWith($client);
$url->__toString()->willReturn('http://example.com');
$data = ['foo' => 'bar'];
$response->getStatusCode()->willReturn(200);
$client->request('POST', $url, ['form_params' => $data])->willReturn($response);
$this->post($url, $data);
assert($this->getResponseStatusCode() === 200); // Failing! :(
}
and the corresponding functions in my class:
public function post(Url $url, array $data)
{
$this->response = $this->client->request('POST', (string) $url, ['form_params' => $data]);
}
public function getResponseStatusCode()
{
return $this->response->getStatusCode();
}
The assertion is failing and when I check what is this status code, I see that instead of the integer 200, it's an instance of PhpSpec\Wrapper\Subject. What am I missing here?
I've searched and googled but cannot find resources about using the mock returned by another mock in phpspec. I'm wondering if the reason for this is that it's a code smell? If so I'd be glad to see how I could do this differently (currently I cannot see how I could keep the code simple and doing differently).
try:
assert($this->getResponseStatusCode()->getWrappedObject() === 200);
this:
$response->getStatusCode()->willReturn(200)
returns a '200' string wrapped in an Subject object, on which you can then make mock/stub calls if needed. To get the real value of the subject you need to call getWrappedObject
I have an action method already written in my web api 2.0 project. I would like to add a new parameter without disturbing the existing contract. What is the best way to do that? Appreciate any best practice hints on this :)
Here's the code sample of what I intend to do:
Existing code:
[Route("{myId}",Name="MyId")]
Public IHttpActionResult Get(String myId)
{
//Some more code here
}
Url: http://localhost:8888/webapi/1111
Expecting to do something like the below:
//I want to keep the route name same for backwards compatibility.
[Route("{myId}/{myName}",Name="MyId")]
Public IHttpActionResult Get(String myId,string? myName)
{
//Some more code here
}
Url: http://localhost:8888/webapi/1111/John
The Url mentioned above hits the method rightly, but I never get the second parameter (myName) populated with John.
Thanks everyone for any help towards this.
Sree.
In your example you have myName as string? which is not allowed as:
The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
A test controller was created to implement you action
[RoutePrefix("webapi")]
public class TestsController : ApiController {
[HttpGet]
[Route("{myId}/{myName}", Name = "MyId")]
public IHttpActionResult Get(string myId, string myName) {
//Some code to show the values of the parameters
return Ok(new { myId = myId, myName = myName });
}
}
When tested with webapi/1111/John the following response is returned
{"myId":"1111","myName":"John"}
which does include the value for MyName as John
If backwards uri webapi/1111 is tried, a NotFound response is returned as the template does not match the new action.
To fix this you need to make the myName parameter optional. To learn more about that check
Optional URI Parameters and Default Values
The new route will look like
//NOTICE THE `?` ON THE {myName} TEMPLATE
[Route("{myId}/{myName?}", Name = "MyId")]
public IHttpActionResult Get(string myId, string myName = null) {...}
You will notice that myName was made optional in the route {myId}/{myName?} and in the action parameter (string myId, string myName = null)
Now when tested with webapi/1111 the following response is returned
{"myId":"1111","myName":null}
Which would match your expected result for backwards compatibility.
String is a reference type so you don't need to make it nullable, it already is. Remove the '?' and remove the Name from the attribute. What happens then?
I'm using genemu_jqueryselect2_entity for a multiple selection field within a form (located in an Sonata admin class) for a so called Uni (university) entity:
->add('courses', 'genemu_jqueryselect2_entity',array('multiple' => true, 'class' => 'PROJECT\UniBundle\Entity\Course'))
But the selected entries are not filled into my entity. With firebug I was able to detect, that the ids of the courses are passed correctly via POST.
Maybe the field is not correctly mapped to the Uni entity, but I have no idea why.
This is the adding method of my Uni entity, which doesn't even get called:
public function addCourse(\PROJECT\UniBundle\Entity\Course $courses)
{
$this->courses[] = $courses;
return $this;
}
How can I get the field to be mapped with the courses attribute of Uni? How could I debug this?
Any help will be appriciated!
Try writing that method like this:
public function addCourse(\PROJECT\UniBundle\Entity\Course $course)
{
$this->courses[] = $course;
$course->setUniversity($this); // Or similar.
return $this;
}
Otherwise foreign key is not set on a course row in the DB.
Try to create method setCourses
public function setCourses(\Doctrine\Common\Collections\Collection $courses)
{
$this->courses = $courses;
...
I don't know why, but the method addCourse isn't called.
Anyway, Tautrimas Pajarskas's answer was usefull to me so I gave an upvote.
The foreign key relationship was the necessary and missing part of my code.
I implemented it in the university sonata admin like this:
private function addUniToCourses ($university) {
foreach($university->getCourses() as $course) {
if(!$course->getUniversities()->contains($university)) {
$course->addUniversity($university);
}
}
}
public function prePersist($university) {
$this->addUniToCourses($university);
}
public function preUpdate($university) {
$this->addUniToCourses($university);
}
This was the solution to my problem.
I had the same problem a while ago: Symfony2, $form->bind() not calling adder methods of entity
Solution:
For the adder (addCourse()) to be called, you have to disable the by_reference option of the field:
->add('courses', 'genemu_jqueryselect2_entity',
array(
'by_reference' => false, // This line should do the trick
'multiple' => true,
'class' => 'PROJECT\UniBundle\Entity\Course'))
I've created simple ViewHelper with http://blog.evan.pro/creating-a-simple-view-helper-in-zend-framework-2. How to get URL params in this helper? $this->params('param') works only in controllers...
Given the code from the blog post, you can use this code from inside the view helper:
$this->request->getPost('param'); // post parameter
// or
$this->request->getQuery('param'); // query parameter
The code from the example receives an instance of the Zend\Http\Request object for the current request and stores it in the property called request of the view helper so you can use the request property to access the Request object and information from it.
In view helper you have to add code like this:
Module.php
'factories' => array(
'myViewHelper' => function($pm) {
return new MyView($pm);
},
)
Now in Helper Class file your have to add following piece of code
public function __construct($pm) {
$this->pluginManager = $pm;
$this->serviceLocator = $this->pluginManager->getServiceLocator();
$this->routeMatch = $this->serviceLocator->get('Router')->match($this->serviceLocator->get('Request'));
}
public function __invoke() {
$params = $this->getRouteMatch()->getParams();
}
Here $params will return all route params in array formate.
so, using urlManager i've been able to get to this point:
'people/<id:\d+>/<last:.*?>/<name:.*?>'=>'person/view',
which generates this: http://foo.com/people/3/smith/john
I want to eliminate the ID number, which in this case is "3"
I tried using this:
'people/<last:.*?>/<name:.*?>'=>'person/view', //makes sense right?
however, when i try to navigate to http://foo.com/people/smith/john , I get a server 400 error.
What gives? Should I try modifying the .htaccess file instead?
As requested here is my entire urlManager component:
'urlManager'=>array(
'urlFormat'=>'path',
'showScriptName'=>false,
'rules'=>array(
'about' => array('site/page', 'defaultParams' => array('view' => 'about')),
'contact'=>'site/contact',
'login' =>'site/login',
'/'=>'site/index',
'people/<last:.*?>/<name:.*?>'=>'person/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),
Here is my PersonController.php file:
public function actionView()
{
$person=$this->loadModel();
$this->render('view',array(
'model'=>$person,
));
}
private $_model;
public function loadModel()
{
if($this->_model===null)
{
if(isset($_GET['last']))
{
$this->_model=Person::model()->findByPk($_GET['id']);
}
if($this->_model===null)
throw new CHttpException(404,'The requested page does not exist.');
}
return $this->_model;
}
Your view action needs to accept the names and do a find by the attributes instead of using loadModel, which requires the ID:
public function actionView($last, $name)
{
$person = Person::model()->find(
'name=":name" AND last=":last"',
array(':name'=>$name, ':last'=>$last)
);
if($person === null) throw new CHttpException(404);
$this->render('view',array(
'model'=>$person,
));
}
Do note that your request will not support multiple people with same first and last names!
You probably need a rewrite condition for URLs so requests are still routed back to index.php.
check out this guide: http://www.yiiframework.com/doc/guide/1.1/en/topics.url#user-friendly-urls
look at the actionView of the PersonController
public function actionView($id,...)
{
}
So the view action must need an $id parameter for it to work. Otherwise it throws a 400 bad request error. In your modified url rule the only parameters specified are $last and $name. The solution is to remove the $id parameter to actionView() if it is not used inside actionView. Otherwise you have to change function logic for determining the required person from $last and $name instead of $id