Parent controller not resolving parameter "User $user", but "$id" - php

I have a parent controller "UserController", and a child controller "UserCartController". Unlike all other controllers, FOSRest refuses the following definition:
class UserController extends AbstractFOSRestController
{
// [...]
/**
* #param User $user
* #return View
*/
public function getUserAction(User $user): View
{
return new View([]);
}
}
This results in the following error:
Every parent controller must have `get{SINGULAR}Action(\$id)` method where {SINGULAR} is a singular form of associated object in [...]/config/../src/GDS/[BundleNameHere]/Resources/config/user_routes.yaml (which is being imported from "[...]/config/routes.yaml"). Make sure there is a loader supporting the "rest" type.
Once I change the head of the way as follows...
class UserController extends AbstractFOSRestController
{
// [...]
/**
* #param User $user
* #return View
*/
public function getUserAction($id): View
{
return new View([]);
}
}
The error no longer appears. What is the reason? In all other controllers, you can resolve model instances.
As a result, my routes don't look very elegant. :)
/api/users/{id}.{_format}
# vs.
/api/carts/{cart}.{_format}
Thanks in advance!

Related

Abstract factory class returning an interface type, method not found?

I am attempting to use the abstract factory pattern. I've created a class, FactoryProducer, that creates a class-specific factory based on a string passed into one of the two class methods.
The issue I'm having is that I've extended one of the concrete factory classes, but the FactoryProducer returns an interface type that doesn't include that method. VS Code is saying that the method doesn't exist. Here's the relevant code
Factory Producer Class
/**
* Creates database or model factory.
*/
class FactoryProducer {
/**
* Creates a factory for the Model classes based on the given function argument.
*
* #param string $type The model class (e.g. 'asset', 'employee')
* #return ModelFactoryInterface The given model's factory.
*/
public static function getModelFactory(string $type) {
switch($type) {
case 'asset':
return new \Inc\Models\AssetModelFactory;
break;
case 'application':
//code here
break;
}
}
}
Concrete Factory Class AssetModelFactory
/**
* The factory for the Asset class.
*/
class AssetModelFactory implements ModelFactoryInterface {
/**
* Create an empty Asset class object.
*
* #return Asset
*/
function create(): Asset {
return new Asset();
}
/**
* Creates an Asset object instantiated with the given properties.
*
* #param array $props The properties for the class.
* #return void
*/
function createWithProps(array $props): Asset {
$asset = new Asset();
$keysToCheck = ['name', 'companyName', 'type', 'label', 'location', 'employees', 'key'];
if(\Inc\Base\Helpers::array_keys_exists($keysToCheck, $props)) {
$asset->setProperties($props['name'], $props['companyName'], $props['type'], $props['label'], $props['location'], $props['employees'], $props['key']);
return $asset;
}
else {
return new \WP_Error('incorrect_props', 'You did not include all of the necessary properties.');
}
}
}
The issue I'm having is with the second method, createWithProps(array $props), because the interface doesn't include this method:
/**
* The interface for model classes.
*/
interface ModelFactoryInterface {
/**
* Creates an object that extends AbstractModel
*
* #return AbstractModel
*/
public function create(): AbstractModel;
}
As you can see, the concrete class objects extend an abstract class. Here is the code that is giving the error:
$assetFactory = \Inc\Base\FactoryProducer::getModelFactory('asset');
$asset = $assetFactory->createWithProps($request);
I'm wondering if I've implemented the abstract factory class incorrectly, or if this is expected behavior from VS Code given that the returned concrete class from FactoryProducer is dynamic based on the parameter (e.g. I've passed 'asset' into the FactoryProducer::getModelFactory method which will, ultimately, return an instance of AssetModelFactory, but the official return type is ModelFactoryInterface).
Thank you in advance for any advice you can provide.
I was able to figure out what I did. I am used to programming languages like C# wherein I can strongly-type the variable prior to declaring it. I ended up refactoring the code so that the factories have methods that return a specific concrete object instead of using a switch statement:
class DBFactory implements DBFactoryInterface {
public static function createAsset(): AbstractDB {
return new \Inc\DB\AssetDB;
}
public static function createApplication(): AbstractDB {
return new \Inc\DB\ApplicationDB;
}
public static function createCompany(): AbstractDB {
return new \Inc\DB\CompanyDB;
}
}

Laravel class Auth

Hi can I ask about this in laravel framework
namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Auth\AuthManager
* #see \Illuminate\Contracts\Auth\Factory
* #see \Illuminate\Contracts\Auth\Guard
* #see \Illuminate\Contracts\Auth\StatefulGuard
*/
class Auth extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'auth';
}
}
what does the return 'auth' exactly returning to the caller ? is it text 'auth' or an object ? and what is the reason why they only have one method in that class ? I apologize i am just learning oop.
Thank you in advance.
In this case as you see method getFacadeAccessor it's returning auth string.
Facades are just "shortcuts" to use other classes but in fact you shouldn't use them everywhere if you don't need to.
In Laravel you can bind objects/classes into Application. So you can write for example:
$app->bind('something', function() {
return new SomeObject();
});
Let's assume there is method doSomething in SomeObject class.
Now you can use this method using:
$app['something']->doSomething();
But you can also create facade:
class GreatClass extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'something';
}
}
and now anywhere in your application you could use:
GreatClass::doSomething();
Answering your question this getFacadeAccessor is returning only the name the name of object that is used when bound to Application. To know how it's used you can look into the source of:
/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
The method you should look first is getFacadeRoot - because this method is returning the requested object.

Custom functions in Doctrine2 auto generated classes

Is there a way to extend classes auto-generated from database by Doctrine2 ?
Example: I have this User class generated by Doctrine.
<?php
namespace Entities;
/**
* User
*/
class User
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $firstName;
/**
* #var string
*/
private $lastName;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
*
* #return User
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName
*
* #param string $lastName
*
* #return User
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
return $this;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
I would like to add this function :
public function getFullName()
{
return $this->getFirstName().' '.$this->getLastname();
}
Is there a cleaner way than adding it directly into this class?
I tried to create another class (Test) in libraries and extends it, then add it in autoload (which is working), but i get an error when I try to save object :
class Test extends Entities\User {
public function getFullName() {
return $this->getFirstName().' '.$this->getLastname();
}
}
Message: No mapping file found named 'Test.dcm.yml' for class 'Test'.
I'm using Doctrine2 in CodeIgniter3.
Thanks.
As explained in the Doctrine 2 FAQ:
The EntityGenerator is not a full fledged code-generator that solves all tasks. [...] The EntityGenerator is supposed to kick-start you, but not towards 100%.
In plain English this means you ask Doctrine to generate the Entity files only once. After that, you are on your own and do whatever changes you like (or it needs) to them.
Because an Entity is not just a container for some properties but it's where the entire action happens, this is how the flow should happen, Doctrine cannot write more code for you.
The only way to add functionality to the stub Entities generated by Doctrine is to complete the generated classes by writing the code that implements the functionality of each Entity according to its role in your Domain Model.
Regarding the other issue, on the Test class, the error message is self-explanatory: any class passed to the EntityManager for handling needs to be mapped.
Take a look at the help page about Inheritance Mapping. You can either map class User as a Mapped Superclass (it acts like a template for the derived classes and its instances are not persisted in the database) or you can use Single Table Inheritance to store the instances of all classes derived from User in a single table (useful when they have the same properties but different behaviour).
Or, in case you created class Test just because you were afraid to modify the code generated by Doctrine, put the behaviour you need in class User and drop class Test.
Seems you are having trouble while accessing the user entity class. You mentioned that test is a library class. Why not try to access the User entity class from a controller. If can do this then may be something is wrong with the configuration of test file. Besides, you need to map you doctrine entity class properly. You can have a look here to learn about doctrine mapping using yml: http://doctrine-orm.readthedocs.org/en/latest/reference/yaml-mapping.html
you can do this:
<?php
namespace Entities;
/**
* User
*/
class User extends Test
{
//... and extends Test
}
or
<?php
namespace Entities;
/**
* User
*/
class User
{
//...
public function getFullName() {
return $this->getFirstName().' '.$this->getLastname();
}
}
view more
Symfony 2 - Extending generated Entity class
http://www.theodo.fr/blog/2013/11/dynamic-mapping-in-doctrine-and-symfony-how-to-extend-entities/
http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html
Annotation allows you to specify repository class to add more methods to entity class.
/**
* #ORM\Entity(repositoryClass="App\Entity\UserRepository")
*/
class User
{
}
class UserRepository extends EntityRepository
{
public function getFullName() {
return $this->getFirstName().' '.$this->getLastname();
}
}
// calling repository method
$entityManager->getRepository('User')->getFullName();
Here's a link [http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html]
7.8.8. Custom Repositories

Yii2: Post complex models

I have a complex entity, looking similar like this:
class Article extends \yii\db\ActiveRecord {
public $id;
public $name;
/** #var ArticleAspectValue[] */
public $aspects;
public function getArticleAspectValues() {
return $this->hasMany(ArticleAspectValue::className(), ['article_id' => $this->id]);
}
}
And I have an entity serving as part of the more complex entity above.
class ArticleAspectValue extends \yii\db\ActiveRecord {
public $aspect_id; // <--- Two-attributes identifier (two-col PK in db)
public $article_id; // <----'
public $value;
}
While every ArticleAspectValue is assigned to ArticleAspect and Article as well, one article only has its own ArticleAspectValues.
The model Article consists of an id, a name and an array of sub-entities called ArticleAspectValues.
I solved the creation of input fields for each ArticleAspectValue, but since this is a simple for-each on the frontend with no connection to the model behind.
Question: How has the form and the receiving controller method to look like in order to post new values on the sub-entities, but according to their superior model, the Article?
PS the doc on complex models is TBD
The solution is to save the inferior model in the controller method as well as the superior, if they are ActiveRecords as well.
Following the appreciated comment from Mihai P., I repost my corrected example code from above and the solution.
The major model of the complex structure looks like this:
/**
* The superior class in a complex model.
* #property int $id
* #property string $name
*
* #property ArticleAspectValues $aspects
*/
class Article extends \yii\db\ActiveRecord
{
/**
* #return \yii\db\ActiveQuery
*/
public function getArticleAspectValues() {
return $this->hasMany(ArticleAspectValue::className(), ['article_id' => 'id']);
}
}
In Yii, properties of an ActiveRecord are created of database table columns. Yii provides magic getters to obtain their values. To work with properties which aren't actually existing, you can annotate them in the class. Most IDE's would parse these annotations and provide them as regular items of a class. Same works for methods.
The inferior class of the complex model looks like this:
/**
* The inferior class of the complex model.
* #property int $aspect_id
* #property int $article_id
* #property string $value
*/
class ArticleAspectValue extends \yii\db\ActiveRecord
{
/**
* #return \yii\db\ActiveQuery
*/
public function getArticle() {
return $this->hasOne(Article::className(), ['article_id' => 'id]);
}
}
In the controller of the superior model, the inferiors are saved within at the same time.
class ArticleController extends \yii\db\ActiveRecord
{
// ...
// Exemplary method. Goes for create action as well.
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
if ($model->aspects->load(Yii::$app->request->post()) && $model->aspects->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
}
}
The load method of ActiveRecords is able to parse a whole post data body to obtain its own values.
It's probably better to extend load and save methods of your complex model to process the inferiors, rather than instruct the controller for each inferior model.

zf2 mvc event-listener or strategy

im new to Zf2, i recently upgraded from zf1 and need help and advice on this problem.
Here the fact :
I'm working on medical project (which is an upgrade to a zf1 version) in some controller (page) i need to have the patient's info and current visitation in sidebar panel...
I know i'm new to zf2 but i don't want to do redundant things like having in every action the getvisiteService() and patientService() retrieve info and passing these results to view over and over.
I thought about a plugin but again i have to pass from controller to view and supercharge my view with partials and placeholder helper (grr!!!)
Thinkin' about Strategy and eventlistener but i don't know how these work and i need to inject result to a partial.
So there is a simple and/or complicated way to achieve that? Thank you in advance any hint and code will be appreciated and sorry for my poor english i speak french (such a typical excuse :) )
There's a ton of approaches you could use here, but sticking to your original question, it's quite easy to inject things into your layout model, with something like this:
Module.php
/**
* On bootstrap event
*
* #param \Zend\Mvc\MvcEvent $e
*/
public function onBootstrap(MvcEvent $e)
{
// Inject something, like a nav into your Layout view model
$viewModel = $e->getViewModel(); // Layout View Model
$navigation= new ViewModel(array(
'username' => 'Bob' // Dynamically set some variables..
));
$navigation->setTemplate('navigation/mynav');
$viewModel->addChild($navigation, 'navigation');
}
You could also create a custom view Helper to do the work for you if you wanted
<?php
/**
* MyHelper.php
*/
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
class MyHelper extends AbstractHelper implements ServiceManagerAwareInterface
{
/**
* Invoke
*
* #return string
*/
public function __invoke()
{
// Dynamically build your nav or what ever.
$patientService = $this->getServiceManager()->get('PatientService');
return 'soemthing';
}
/**
* #var ServiceManager
*/
protected $serviceManager;
/**
* Retrieve service manager instance
*
* #return ServiceManager
*/
public function getServiceManager()
{
return $this->serviceManager;
}
/**
* Set service manager instance
*
* #param ServiceManager $locator
* #return User
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
return $this;
}
}

Categories