Laravel ::__construct() must implement interface - php

I'm using Laravel 5.4 And I'm trying to inject a $order class into a trait that's going to implemented by a model. Like this:
class Forum extends Model
{
use Orderable;
The constructor of the trait looks like this:
trait Orderable
{
public $orderInfo;
public function __construct(OrderInterface $orderInfo)
{
$this->orderInfo = $orderInfo;
}
My service provider looks like this:
public function register()
{
$this->app->bind(OrderInterface::class, function () {
return new Order(new OrderRepository());
});
$this->app->bind(OrderRepositoryInterface::class, function () {
return new OrderRepository();
});
}
The constructor of my Order class looks like this:
public function __construct(OrderRepositoryInterface $orderInfo)
{
$this->orderInfo = $orderInfo;
}
But I receive the error:
Type error: Argument 1 passed to App\Forum::__construct() must implement interface Project\name\OrderInterface, array given, called in /home/vagrant/Code/Package/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 268
The OrderRepository class is implementing the OrderRepositoryInterface. The Order class is implementing the OrderInterface interface.
App\Forum is the model that uses the Orderable trait.
What could I be doing wrong here?

You are extending Model. This class already has a __construct that you need to use. This __construct expects array $attributes = [] as the first argument.
So in your __construct you also need to have this as the first argument and pass this to the parent class:
public function __construct(array $attributes = [], OrderRepositoryInterface $orderInfo)
{
$this->orderInfo = $orderInfo;
parent::__construct($attributes);
}
However you can work around using __construct in laravel using boot.
For example in a Model:
class Forum extends Model
{
protected static function boot()
{
parent::boot();
// Do something
}
}
Or in a Trait:
trait Orderable
{
public static function bootOrderableTrait()
{
static::created(function($item){
// Do something
});
}
}

In PHP it's not possible to have multiple constructors. If you will look to Model:
public function __construct(array $attributes = [])
it expect array. That's why I assume that somewhere in Model array passed to constructor instead of 'OrderInterface'.

Related

Laravel 7: Service provider bind a class with a constructor parameter model

I've created a Service Provider with a class that has a model passed into the constructor.
The model needs to be a specific record based off the $id taking from the URL eg /path/{$id}
How can I use the requested model in the Service Provider?
An option is to pass the model into the execute method but for now I'll need to pass it into the construct.
MyController
class MyController {
public function show(MyClass $myClass, $id)
{
$model = MyModel::find($id);
return $myClass->execute();
}
}
MyClass
class MyClass
{
$private $myModel;
public function __construct(MyModel $myModel)
{
$this->myModel = $myModel;
}
public function execute()
{
//do something fun with $this->myModel
return $theFunStuff;
}
}
MyServiceProvider
public function register()
{
$this->app->singleton(MyClass::class, function ($app) {
return new MyClass(/* How can I use $myModel? */);
});
}
I don't see any value / reason to use a singleton here.
The service provider registers the singleton before your route is resolved, so there is no way to pass the $model from the controller into the register method. I would remove the service provider and do the following:
From the docs:
If some of your class' dependencies are not resolvable via the
container, you may inject them by passing them as an associative array
into the makeWith method:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
So in your case something like this:
public function show($id)
{
return app()->makeWith(MyClass::class, ['myModel' => MyModel::find($id)])->execute();
}
Or shorter with the help of route model binding:
public function show(MyModel $myModel)
{
return app()->makeWith(MyClass::class, compact('myModel'))->execute();
}
Note that the argument names passed to makeWith have to match the parameter names in the class constructor.

How to pass parameter to a laravel elequent model's event observer

I have a model in laravel and I want to do something after the first time which an object of my model is created. the simplest way is to add a static boot method inside my model's class like the code below:
class modelName extends Model
{
public static function boot()
{
parent::boot();
self::created(function ($model) {
//the model created for the first time and saved
//do something
//code here
});
}
}
so far so good! the problem is: the ONLY parameter that created method accepts is the model object itself(according to the documentation) :
Each of these methods receives the model as their only argument.
https://laravel.com/docs/5.5/eloquent#events
I need more arguments to work with after model creation. how can I do that?
Or is there any other way to do something while it's guaranteed that the model has been created?
laravel version is 5.5.
You're close. What I would probably do would be to dispatch an event right after you actually create the model in your controller. Something like this.
class WhateverController
{
public function create()
{
$model = Whatever::create($request->all());
$anotherModel = Another::findOrFail($request->another_id);
if (!$model) {
// The model was not created.
return response()->json(null, 500);
}
event(new WhateverEvent($model, $anotherModel));
}
}
I solved the issue using static property in eloquent model class:
class modelName extends Model
{
public static $extraArguments;
public function __construct(array $attributes = [],$data = [])
{
parent::__construct($attributes);
self::$extraArguments = $data ;
public static function boot()
{
parent::boot();
self::created(function ($model) {
//the model created for the first time and saved
//do something
//code here
self::$extraArguments; // is available in here
});
}
}
It works! but I don't know if it may cause any other misbehavior in the application.
Using laravel events is also a better and cleaner way to do that in SOME cases.but the problem with event solution is you can't know if the model has been created for sure and it's time to call the event or it's still in creating status ( and not created status).

Call createForm() and generateUrl() from service in Symfony2

I would like have access to controller methods from my custom service. I created class MyManager and I need to call inside it createForm() and generateUrl() functions. In controller I can use: $this->createForm(...) and $this->generateUrl(...), but what with service? It is possible? I really need this methods! What arguments I should use?
If you look to those two methods in Symfony\Bundle\FrameworkBundle\Controller\Controller class, you will see services name and how to use them.
public function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
{
return $this->container->get('router')->generate($route, $parameters, $referenceType);
}
public function createForm($type, $data = null, array $options = array())
{
return $this->container->get('form.factory')->create($type, $data, $options);
}
Basically, you class need services router and form.factory for implementing functionality. I do not recommend passing controller to your class. Controllers are special classes that are used mainly by framework itself. If you plan to use your class as service, just create it.
services:
my_manager:
class: Something\MyManager
arguments: [#router, #form.factory]
Create a constructor with two arguments for services and implement required methods in your class.
class MyManager
{
private $router;
private $formFactory;
public function __construct($router, $formFactory)
{
$this->router = $router;
$this->formFactory = $formFactory;
}
// example method - same as in controller
public function createForm($type, $data = null, array $options = array())
{
return $this->formFactory->create($type, $data, $options);
}
// the rest of you class ...
}
assuming you are injecting the service into your controller , you can pass the controller object to your service function
example
class myService
{
public function doSomthing($controller,$otherArgs)
{
$controller->generateForm();
}
}
class Mycontroller extends Controller
{
public function indexAction()
{
$this->get("my-service")->doSomthing($this,"hello");
}
}

Non-Static Method should not be called Statically

I am using a repository pattern and am trying to establish relationships between models. When I try to run the store() method (in the controller) which is trying to use the user() method (which establishes the relationship with the Party model), I get the following error message:
Non-static method Party::user() should not be called statically, assuming $this from incompatible context
I don't understand why I get this error when I try to run the user() relationship method, but all of the other methods (including $this->party->all(), $this->party->create($data)), work just fine.
Here is the relevant code:
// PartiesController.php
public function __construct(Party $party){
$this->party = $party
}
public function store(){
$data = Input::all();
$user = Sentry::getUser();
$this->party->user()->create($data);
}
// Party.php
class Party extends Eloquent{
public function user(){
return $this->belongsTo('User');
}
}
// User.php
use Cartalyst\Sentry\Users\Eloquent\User as SentryUserModel;
class User extends SentryUserModel implements UserInterface, RemindableInterface {
public function party(){
return $this->hasMany('Party');
}
}
// PartyRepository.php
namespace repositories\Party;
interface PartyRepository{
public function all();
public function findByID($id);
public function create($input);
public function user();
}
// EloquentPartyRepository.php
namespace repositories\Party;
use Party;
class EloquentPartyRepository implements PartyRepository{
public function all(){
return Party::all();
}
public function create($input){
return Party::create($input);
}
public function user(){
return Party::user();
}
}
The issue is because you are calling a non-static method in a static context. You may be used to seeing the way Laravel does a lot of this (e.g. User::find() and the like). These, in reality though, are not static calls (a class instance is actually being resolved behind the scenes and the find() method invoked on that instance).
In your case, it is just a plain static method call. PHP would allow this, except for the fact that in the method you are referencing $this and PHP doesn't know what to do with it. Static method calls, by definition, have no knowledge of any instances of a class.
My advice would be to inject an instance of your Model class into your repository's constructor, something like this:
//Class: EloquentPartyRepository
public function __construct(Party $party)
{
$this->party = $party;
}
public function user($partyId)
{
return $this->party->find($partyId)->user();
}
The Party instance you send to the constructor should not be a record from the database, just an empty instance of Party (i.e. new Party()), though I believe if you just add it to the constructor, the IoC should be able to leverage dependency injection and provide you with an instance.
An equivalent implementation is here, that adds a byId method:
//Class: EloquentPartyRepository
public function __construct(Party $party)
{
$this->party = $party;
}
public function byId($partyId)
{
return $this->party->find($partyId);
}
public function user($partyId)
{
if($party = $this->byId($partyId)) {
return $party->user();
}
return null;
}
I have solved the problem. Thank you #watcher and #deczo for your feedback. Both were very helpful and relevant to this error message.
In the end, I only needed to change one line. I had the sequence of method calls out of order in the store() function. Here is the relevant code.
// PartiesController.php
public function store(){
$data = Input::all();
$user = Sentry::getUser();
$user->party()->create($data);
}
In my case, to remove the non-static error and to properly insert the User model into the Party model, I only had to make the aforementioned change.
I referred to http://laravel.com/docs/eloquent/#inserting-related-models for the appropriate sequence.

ZF2 : Call a service from an external class?

In my Zend Framework 2 project, I have an external lib and I want to save my information in the base with the model.
....
....
....
EDITED MESSAGE :
I explain again my need: In my controllers, I make insertions and deletions in the database and I want to log all actions in a "t_log" table . To do it, I have thought to create an external class.
My question is: How I can call my models method from my external class ?
namespace Mynamespace;
use Firewall\Model\Logs;
use Firewall\Model\LogsTable;
class StockLog
{
public function addLog()
{
$log = $this->getServiceLocator()->get('Firewall\Model\LogTable');
$log->save('user added');
die('OK');
}
}
My model :
namespace Firewall\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Sql\Select;
class UserGroupTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function save()
{
// How I Can call this method from the StockLog method ?
}
}
Thanks you !
getServiceLocator is a function of \Zend\Mvc\Controller\AbstractActionController so it is supposed to be used in your controllers.
I dont know what your StockLog class is, but it is not extending any other class, so i guess it has not that function and your error is one step before, in the call to getSErviceLocator that is not defined, so its not returning an object.
Probably you can inject the service locator with something like
class StockLog
{
private $serviceLocator= null;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function add()
{
# Do you know how I can call the service ??
$User = $this->serviceLocator->get('Firewall\Model\UserTable');
}
}
and then, when you create your StockLog object, in your controller, you inject the servicelocator
public class yourController extends AbstractActionController {
public function yourAction(){
$mStockLog = new StockLog ();
$mStockLog->setServiceLocator($this->getServiceLocator());
/...
}
}
Also, if you only need the 'Firewall\Model\UserTable' service, you should inject just that, instead of the serviceLocator.
At any rate you should minimice the knowledge of your model classes about the rest of the system, hving always in mind the dependency inversion principle, to get a better decoupling
UPDATE
inject the log table
namespace Mynamespace;
use Firewall\Model\Logs; use Firewall\Model\LogsTable;
class StockLog {
private $logTable= null;
public function setLogTable($logTable)
{
$this->logTable= $logTable;
}
public function addLog()
{
$this->logTable->save('user added');
die('OK');
}
}
and then, when you create your StockLog (in your controller, or wherever you do it, before you use it) you inject the logtable object
$mStockLog = new StockLog ();
$mStockLog->setLogTable($this->getServiceLocator()->get('Firewall\Model\LogTable'));
Of course, Im suposing that you configured correctly your Firewall\Model\LogTable class to be retrieved by means of the service manager, in getServiceConfig() in your Module.php
public function getServiceConfig() {
return array (
'factories' => array (
'Firewall\Model\LogTable' => function ($sm) {
$logTable = //create it as you use to
return $logTable;
}
)
}

Categories