I am developing an API under symfony4 and I wish I could create a parent controller that I could use to call functions that would be repeated in another controller. Here are my controllers that I would like to extend from a parent controller:
My DeliveryController:
<?php
namespace App\Controller;
use App\Entity\DeliveryMan;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class AuthController
* #package App\Controller
* #Route("/api")
*/
class DeliveryController extends AbstractController
{
/**
* #Route(
* name="api_delivery_man_post",
* path="/delivery_man",
* methods={"POST"},
* defaults={
* "_api_resource_class"=DeliveryMan::class,
* "_api_collection_operation_name"="post"
* }
* )
*/
public function postAction(DeliveryMan $data, UserPasswordEncoderInterface $encoder): DeliveryMan
{
return $this->encodePassword($data, $encoder);
}
protected function encodePassword(DeliveryMan $data, UserPasswordEncoderInterface $encoder): DeliveryMan
{
$encoded = $encoder->encodePassword($data, $data->getPassword());
$data->setPassword($encoded);
return $data;
}
}
My AuthController:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Entity\User;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* Class AuthController
* #package App\Controller
* #Route("/api")
*/
class AuthController extends AbstractController
{
/**
* #Route(
* name="api_users_post",
* path="/users",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="post"
* }
* )
*/
public function postAction(User $data, UserPasswordEncoderInterface $encoder): User
{
return $this->encodePassword($data, $encoder);
}
protected function encodePassword(User $data, UserPasswordEncoderInterface $encoder): User
{
$encoded = $encoder->encodePassword($data, $data->getPassword());
$data->setPassword($encoded);
return $data;
}
}
As can be seen I call 2 identical actions in 2 different controllers the only difference that there would be the entities and the path of the road.
So I was thinking of creating a ResourceController parent controller that would be extended from AbstractController and that the child controllers would be extended from ResourceController but I do not see how after how to create my methods in my parent controller and retrieve them in the child controllers.
If someone has already done that I am a taker :) Thank you for your help.
EDIT Result ResourceController:
<?php
namespace App\Controller;
use App\Entity\DeliveryMan;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class ResourcesController extends AbstractController
{
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function encodePassword(User $data): User
{
$encoded = $this->encoder->encodePassword($data, $data->getPassword());
$data->setPassword($encoded);
return $data;
}
public function encodePasswordDelivery(DeliveryMan $data): DeliveryMan
{
$encoded = $this->encoder->encodePassword($data, $data->getPassword());
$data->setPassword($encoded);
return $data;
}
}
Just make a ResourceController wich extends Symfony AbstractController.
Write your 2 shared methods here, then in any Controller that extends ResourceController you can call them as you would normally call a class method: using $this
class ResourceController extends AbstractController
{
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function encodePassword(Object $data): Object
{
$encoded = $this->encoder->encodePassword($data, $data->getPassword());
$data->setPassword($encoded);
return $data;
}
}
class AuthController extends ResourceController
{
public function someAction(User $data)
{
return $this->encodePassword($data);
}
}
I also suggest you write an interface with a getPassword method that User and DeliveryMan will implements. Not only you'll ensure that the method is implemented but you' also be able to typehint, say AuthenticatedEntityInterface, instead of Object
Related
How can I do this in PHPStorm so hinting works correctly when calling the model() method?
For instance:
/**
* #property-read \Stores $store
*/
class Items extends CActiveRecord
{
public static function model($className = __CLASS__)
{
return parent::model($className);
}
public function relations()
{
return array('store' => array(self::BELONGS_TO, 'Stores', array('store_id' => 'id')),
}
}
$items = new Items;
$items->store; // PHPStorm type hints this correctly
Items::model()->store; // PHPStorm does NOT type hint correctly.
Model above minimalist for focus of this post.
You just have to 'tell' PhpStorm what this method returns.
/**
* #param $className
* #returns Items
*/
public static function model( $className = __CLASS__ ) {
return parent::model( $className );
}
I am trying to call a parent method from its child which has the same method name. Doing so results in a strict standards error. There's an easy solution of renaming the child method. However, is there a way to keep the names of the two methods identical without a standards warning? Thanks.
Strict standards: Declaration of Child::getContentFromDb() should be compatible with Parent::getContentFromDb($id) in /foo/Child.class.php on line xxx
Pseudo-code example:
class Parent {
protected function getInfoFromDb($id) {
return $infoFromDb;
}
}
class Child extends Parent {
public static $id = xx;
public $info = array();
public function __construct() {
$this->info = $this->getInfoFromDb();
}
public function getInfoFromDb() {
// the line below causes the problem
return parent::getInfoFromDb(self::$id);
}
}
Your method override should take the same parameter list as the one you are overriding.
e.g.
class ParentClass {
protected function getInfoFromDb($id) {
return "INFO FROM DB:" . $id;
}
}
class Child extends ParentClass {
public static $id = "xx";
public $info = array();
public function __construct() {
$this->info = $this->getInfoFromDb();
}
/**
* #param specific ID, or do not set for default action.
* #return string
*/
public function getInfoFromDb($id = false) {
return parent::getInfoFromDb(self::$id);
}
}
I'm effectively trying to define the relationships between users (sender and recipient) and messages.
My Messages migration is:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMessagesTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
//
Schema::create('messages', function($t){
$t->increments('id');
$t->integer('sender_user_id')->unsigned();
$t->integer('recipient_user_id')->unsigned();
$t->string('subject');
$t->text('content');
$t->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
//
Schema::dropIfExists('messages');
}
}
My Message model was straightforward:
<?php
class Message extends Eloquent{
// link to sender user id
public function from(){
return $this->hasOne('User', 'sender_user_id');
}
// link to recipient user id
public function to(){
return $this->hasOne('User', 'recipient_user_id');
}
}
But I'm unsure in defining the hasMany relationships in my User model.
The docs (http://laravel.com/docs/eloquent#relationships) shows the following:
return $this->hasMany('Comment', 'foreign_key');
return $this->hasMany('Comment', 'foreign_key', 'local_key');
Now, I'm confused which key is which in the latter hasMany relationship. Which is correct for my User model?
public function sentMessages(){
return $this->hasMany('Messages', 'id', 'sender_user_id');
}
public function sentMessages(){
return $this->hasMany('Messages', 'sender_user_id', 'id');
}
You have to set your relation like this:
public function sentMessages()
{
return $this->hasMany('Messages', 'sender_user_id');
}
public function receivedMessages()
{
return $this->hasMany('Messages', 'recipient_user_id');
}
I would to override constroller constrcuter's like this :
class XControler extends AppController {
public $attr = null;
public __construct(){
$this->attr = new YController();
}
}
But when I do that I take error ! can you explain me why and how I do that with out using requestAction just OOP !
thanks
Controllers are responsible for dealing with end user requests. Each controller action should have a view, and normally you would not want to access the methods from YController inside XController.
What you want to achieve can be done this way:
XController.php
App::uses('YController', 'Controller');
class XController extends AppController {
public $attr;
public $uses = array('Person');
public function __construct($request = null, $response = null) {
$this->attr = new YController();
parent::__construct($request, $response);
}
public function method1() {
// you can now call methods from YController:
$this->attr->Ymethod1();
}
}
YController.php
class YController extends AppController {
public function Ymethod1() {
// ....
}
}
However, the business logic should be inside Models or Components. This is the proper way to share methods between more controllers.
So your XController should look like:
class XController extends AppController {
public $uses = array('Model1');
public function action1() {
$this->Model1->method1();
// ....
}
}
I appended a xxx function to the class Controller, then I touched a file named 'VideoController'. It's extends Controller.
When I execute the VideoController, the xxx function can't be called, why?
the function ajaxReturn :
class Controller extends CController
{
/**
* #var string the default layout for the controller view. Defaults to '//layouts/column1',
* meaning using a single column layout. See 'protected/views/layouts/column1.php'.
*/
public $layout='//layouts/column1';
/**
* #var array context menu items. This property will be assigned to {#link CMenu::items}.
*/
public $menu=array();
/**
* #var array the breadcrumbs of the current page. The value of this property will
* be assigned to {#link CBreadcrumbs::links}. Please refer to {#link CBreadcrumbs::links}
* for more details on how to specify this property.
*/
public $breadcrumbs=array();
/**
* zhoumengkang
* 从Thinkphp里拖过来的
*/
protected function ajaxReturn($data,$info='',$status=1,$type='JSON') {
$result = array();
$result['status'] = $status;
$result['info'] = $info;
$result['data'] = $data;
if(strtoupper($type)=='JSON') {
header("Content-Type:text/html; charset=utf-8");
exit(json_encode($result));
}elseif(strtoupper($type)=='XML'){
header("Content-Type:text/xml; charset=utf-8");
exit(xml_encode($result));
}elseif(strtoupper($type)=='EVAL'){
header("Content-Type:text/html; charset=utf-8");
exit($data);
}else{
// TODO
}
}
}
but it can't by called in
class VideoController extends Controller {
public function actionTest() {
$this->ajaxReturn(true,'test',1);
}
}
You have to import your controller before extend.
Yii::import('application.controllers.Controller');
class VideoController extends Controller {
public function actionTest() {
$this->ajaxReturn(true,'test',1);
}
}
Better change your controller name from Controller to someothername.