Cakephp 3 : How to detect mobile in controller by requestHandler? - cakephp

I need detect mobile in controller for a condition. I have tried below code in my controller.
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
}
Then I have written below code in index method
if ($this->RequestHandler->is('mobile'))
{
//condition 1
}else {
//condition 2
}
Here I get the error
Error: Call to undefined method Cake\Controller\Component\RequestHandlerComponent::is()
How can mobile detect in controller ?

The request handler isn't necessary for that since all the request handler does is proxy the request object:
public function isMobile()
{
$request = $this->request;
return $request->is('mobile') || $this->accepts('wap');
}
The controller also has direct access to the request object, so the code in the question can be rewritten as:
/* Not necessary
public function initialize()
{
parent::initialize();
}
*/
public function example()
{
if ($this->request->is('mobile')) {
...
} else {
...
}
}

I think that will be
$this->RequestHandler->isMobile()

CakePHP 3 uses mobiledetect/mobiledetectlib lib
In bootstrap.php added 2 types of detection 'mobile', 'tablet'
You can use it:
if ($this->request->is('mobile')) {
// ...
}
elseif ($this->request->is('tablet')) {
// ...
}
else {
// ...
}

Related

How can I mock a validation provider in Cakephp

I have a validator that checks if a vat-number is correct. In order to do that it calls an external service. This external call slows the tests down and is unreliable, so I would like to mock it, but I don't understand how I could do it.
public function validationDefault(Validator $validator)
{
$validator->setProvider('vat', 'App\Model\Validation\VatValidation');
$validator->add('vat_no', 'isValidVatNo', [
'rule' => 'validVatNumber',
'provider' => 'vat',
]);
}
And this is the validation provider:
<?php
namespace App\Model\Validation;
use Cake\Core\Configure;
use Cake\Validation\Validation;
use VatNumberCheck\Utility\Model\VatNumberCheck;
class VatValidation extends Validation
{
public static function validVatNumber($check)
{
$vatNumberCheck = new VatNumberCheck();
try {
return $vatNumberCheck->check($check);
} catch (InternalErrorException $e) {
return false;
}
}
}
public function testValidationFail() {
$VatValidator = $this->getMockBuilder('Cake\Validation\Validator')
->setMethods(['validVatNumber'])
->getMock();
$VatValidator->expects($this->any())
->method('validVatNumber')
->will($this->returnValue(false));
$this->Users->getValidator()->setProvider('vat', $VatValidator);
$user = $this->Users->newEntity([
'vat_no' => 'asdf',
]);
$errors = $user->errors();
$this->assertArrayHasKey('vat_no', $errors);
}

FOSUserBundle: Success target after password reset according to roles

After the user did reset his password using the password reset of FOSUserBundle, by default he is redirected to the FOSUserProfile. I want to redirect to a different route according to their role. Is this possible and if yes, how?
I do this code but it redirect all kind of users
namespace Acme\UserBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Listener responsible to change the redirection at the end of the password resetting
*/
class PasswordResettingListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
];
}
public function onPasswordResettingSuccess(FormEvent $event) {
$url = $this->router->generate('homepage');
$event->setResponse(new RedirectResponse($url));
}
}
And then I registering it as a service with
services:
acme_user.password_resetting:
class: Acme\UserBundle\EventListener\PasswordResettingListener
arguments: [ "#router" ]
tags:
- { name: kernel.event_subscriber }
Based on your version of Symfony you can choose one of the approaches described in: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
For example you can use security.authorization_checker service:
Inject it into your service:
services:
acme_user.password_resetting:
class: Acme\UserBundle\EventListener\PasswordResettingListener
arguments: [ "#router", "#security.authorization_checker" ]
tags:
- { name: kernel.event_subscriber }
Then in your actual service:
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
/**
* Listener responsible to change the redirection at the end of the password resetting
*/
class PasswordResettingListener implements EventSubscriberInterface {
private $router;
private $authorizationChecker;
public function __construct(UrlGeneratorInterface $router, AuthorizationChecker $authorizationChecker) {
$this->authorizationChecker = $authorizationChecker;
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
];
}
public function onPasswordResettingSuccess(FormEvent $event) {
//$url = $this->router->generate('homepage');
//$event->setResponse(new RedirectResponse($url));
if (false === $this->authorizationChecker->isGranted('ROLE_ADMIN')) {
// redirect somewhere
} else {
// redirect elsewhere
}
}
}

How do I create a custom object class that's available to my methods in AngularJS

I'm a huge fan of angular but it's got some tricky concepts with extremely nuanced differences between them and this is one of them.
I just want to create an class that I can use to create custom objects in my Angular controllers and factories. It surely shouldn't be that hard but I can't figure out how to do it. I want to have a custom, ResultSet class which I can instantiate to create instances of ResultSet. However for the life of me I can't figure out the correct syntax of factory v. service to use.
This is all I want:
ResultSet = function(dataSet){
this.filter = function(){
# filters and returns dataSet
# ...
}
}
and then I want to be able instantiate an instance of ResultSet inside a controller etc:
MyApp.controller('pageCtrl', ['ResultSet', (ResultSet) ->
# ...
rs = ResultSet.new(dataToFilter)
How can I create a service that allows me to create instances of my custom object?
It seems more correct to use an Angular Service rather than a Factory since a service returns an instance of an object (which is exactly what I want). But I can't figure out how to do this...
How would I use a service to declare my custom ResultSet class and then how would I instantiate an instance from it?
Maybe you were looking for something like this:
.factory('User', function (Organisation) {
/**
* Constructor, with class name
*/
function User(firstName, lastName, role, organisation) {
// Public properties, assigned to the instance ('this')
this.firstName = firstName;
this.lastName = lastName;
this.role = role;
this.organisation = organisation;
}
/**
* Public method, assigned to prototype
*/
User.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName;
};
/**
* Private property
*/
var possibleRoles = ['admin', 'editor', 'guest'];
/**
* Private function
*/
function checkRole(role) {
return possibleRoles.indexOf(role) !== -1;
}
/**
* Static property
* Using copy to prevent modifications to private property
*/
User.possibleRoles = angular.copy(possibleRoles);
/**
* Static method, assigned to class
* Instance ('this') is not available in static context
*/
User.build = function (data) {
if (!checkRole(data.role)) {
return;
}
return new User(
data.first_name,
data.last_name,
data.role,
Organisation.build(data.organisation) // another model
);
};
/**
* Return the constructor function
*/
return User;
})
From this post by Gert Hengeveld.
myApp.factory('ResulSet', function() {
function ResultSetInstance(dataSet) {
this.filter = function(){
// ...
}
}
return {
createNew: function(dataSet) {
return new ResultSetInstance(dataSet);
}
};
});
and then
myApp.controller('pageCtrl', function(ResultSet) {
var someData = ...;
var rs = ResultSet.createNew(someData);
}
Edit (from the question asker)
On experimenting with this further I found that you didn't even need to have the createNew method.
myApp.factory('ResultSetClass', function() {
ResultSetClass = function(dataSet) {
this.filter = function(){
// ...
}
}
return ResultSetClass
});
works just fine and then you can call new ResultSetClass(args).
Note for those using Coffeescript
Coffeescript will return the last variable or method in your class instance so if you are using coffeescript (as a general rule), it's imperative to return this at the end of the class definition
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
return this
return ResultSetClass
If you don't return this explicitly then you'll find that when you call
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
then you'll simply be left with the last thing the coffeescript returns which is the filter method.
I recently has do do something like that because I wanted to implement a factory of class instance, and being able to configurate my instances and benefit from Angular Dependency injection. I ended up with something like that
// Implem
export class XAPIService {
private path: string;
/* this DO NOT use angular injection, this is done in the factory below */
constructor(
private seed: XAPISeed,
private $http: ng.IHttpService,
private slugService: SlugService
) {
const PATH_MAP: Map<Y, Z> = new Map([
['x', id => `/x/${id}`],
['y', id => `/y/${id}`],
]);
this.path = PATH_MAP.get(this.seed.type)(this.seed.id);
}
list() {
/* implem that use configured path */
return this.slugService
.from(this.path + `/x`)
.then(url => this.$http.get<IX>(url))
.then(response => response.data)
}
}
export type IXAPIFactory = (s: XAPISeed) => XAPIService;
export function XAPIFactory(
$http: ng.IHttpService,
myService: SlugService
) {
'ngInject';
return (seed: XAPISeed) =>
new XAPIService(seed, $http, myService);
}
// angular
angular.module('xxx', [])
.factory('xAPIFactory', XAPIFactory)
// usage in code
export class XsController implements ng.IComponentController {
/* #ngInject */
constructor(
private xAPIFactory: IXAPIFactory,
) {}
$onInit() {
this.xService = this.xAPIFactory({ id: 'aaabbbaaabbb', type: 'y' });
return this.xService.list()
.then(xs => {
this.xs = xs;
})
}
}

Respond with both body and status code in Nancy

I'm new to Nancy and I want to return both a custom HttpStatusCode and a body (content). If I return an HttpStatusCode, it returns it with a blank body. If I return a string then it returns that as the body but always with a 200 status code of OK.
public class SendSMS : NancyModule
{
public SendSMS()
{
Post["/SendSMS"] = parameters =>
{
return HttpStatusCode.BadRequest; // this works, no body
return "Missing \"to\" parameter"; // this works, 200 status code
// want to return status code with message
};
}
}
You could always create an instance of the Response type and set the Body and StatusCode yourself. If you wanted to take a shortcut you could do something like
var r = (Response)"Some string that goes into the body";
r.StatusCode = 123;
return r;
This should work.
public class SendSMS : NancyModule
{
public SendSMS()
{
Post["/SendSMS"] = parameters =>
{
return Negotiate.WithModel("Missing \"to\" param")
.WithStatusCode(HttpStatusCode.BadRequest)
};
}
}
For more information check the docs on controlling content negotiation.
This is the simplest way I've found:
Return from your Module:
return new Response {
StatusCode = HttpStatusCode.NotFound, ReasonPhrase = "Resource not found"
};
If you have problems with encoding it's better to use
return new TextResponse(HttpStatusCode.STATUS, "Text Responsé")

cakephp render + afterfilter variables are unset

I am trying to use render for specific action names in my app.
Actually, here are my condition set in my AppController::afterFilter()
if($this->action == 'parameter') {
$this->render('/Elements/parameter');
}
else if($this->action == 'datagrid') {
$this->render('/Elements/datagrid');
}
And in my controller /samples/parameter :
$this->set('model', Inflector::singularize(Inflector::camelize($this->name)));
$this->set('controller', $this->name);
if($parameter_id) {
$this->set('mode', $mode);
$this->set('parameter', $this->Sample->find('first', array('conditions' => array('Sample.id' => $parameter_id))));
} else {
$this->set('mode', 'add');
$this->set('parameter', array());
}
I know that I have to render AFTER the definition of variables, so I use afterFilter Something I don't understand or missed ?
Infos:
I have set in Samples Controller the function
public function afterFilter(){
parent::afterFilter();
}
Thank you all!
The afterFilter() callback is called after rendering process is done, so calling render() inside it is doing it wrong.
If you want to change the view to be rendered do so in beforeRender(). So do something like
if ($this->action == 'parameter') {
$this->view = '/Elements/parameter';
} elseif ($this->action == 'datagrid') {
$this->view = '/Elements/datagrid';
}

Resources