PHP Laravel: Trait not found - php

I have some trouble with namespace and use.
I get this error: "Trait 'Billing\BillingInterface' not found"
These are the files in my Laravel application:
Billing.php
namespace Billing\BillingInterface;
interface BillingInterface
{
public function charge($data);
public function subscribe($data);
public function cancel($data);
public function resume($data);
}
PaymentController.php
use Billing\BillingInterface;
class PaymentsController extends BaseController
{
use BillingInterface;
public function __construct(BillingPlatform $BillingProvider)
{
$this->BillingProvider = $BillingProvider;
}
}
How to i use use and namespace properly?

BillingInterface is an interface not a trait. Thus it can't find the non existent trait
Also you have an interface called BillingInterface in a namespace called Billing\BillingInterface, the fully qualified name of the interface is: \Billing\BillingInterface\BillingInterface
Perhaps you mean
use Billing\BillingInterface\BillingInterface;
// I am not sure what namespace BillingPlatform is in,
// just assuming it's in Billing.
use Billing\BillingPlatform;
class PaymentsController extends BaseController implements BillingInterface
{
public function __construct(BillingPlatform $BillingProvider)
{
$this->BillingProvider = $BillingProvider;
}
// Implement BillingInterface methods
}
Or to use it as a trait.
namespace Billing;
trait BillingTrait
{
public function charge($data) { /* ... */ }
public function subscribe($data) { /* ... */ }
public function cancel($data) { /* ... */ }
public function resume($data) { /* ... */ }
}
Again the modified PaymentsController, but with fully qualifies names.
class PaymentsController extends BaseController
{
// use the fully qualified name
use \Billing\BillingTrait;
// I am not sure what namespace BillingPlatform is in,
// just assuming it's in billing.
public function __construct(
\Billing\BillingPlatform $BillingProvider
) {
$this->BillingProvider = $BillingProvider;
}
}

Related

Why I am unable to assert that method has been called on my mocked service?

I have made a simple class named MyService
namespace App\Services;
class MyService
{
private $anotherService;
public function setService(AnotherService $anotherService)
{
$this->anotherService = $anotherService;
}
public function getService()
{
if(empty($this->anotherService)){
$this->setService(new AnotherService());
}
return $this->anotherService;
}
public function call()
{
$anotherService = $this->getService();
$anotherService->SetXY(5,10);
}
}
As you can se via a setter I set as Depedency the AnotherService:
namespace App\Services;
class AnotherService
{
public function SetXY($x,$y)
{
}
}
In order to test whether the MyService runs as expected I made the following test:
namespace Tests\Services;
namespace Tests\Services;
use App\Services\MyService;
use App\Services\AnotherService;
use PHPUnit\Framework\TestCase;
use Mockery;
class MyServiceTest extends TestCase
{
public function testService()
{
$mockedAnotherService = Mockery::spy(AnotherService::class);
$mockedAnotherService->shouldReceive('SetXY');
$service = new MyService();
$service->setService($mockedAnotherService);
$service->call();
$mockedAnotherService->shouldHaveReceived()->setXY(5,10);
}
}
But for some reason seems that I am unable to assert that setXY is called despite the opposite. The error is:
1) Tests\Services\MyServiceTest::testService
Mockery\Exception\InvalidCountException: Method setXY(<Any Arguments>) from Mockery_0_App_Services_AnotherService should be called
at least 1 times but called 0 times.
/var/www/html/api/vendor/mockery/mockery/library/Mockery/CountValidator/AtLeast.php:47
/var/www/html/api/vendor/mockery/mockery/library/Mockery/Expectation.php:310
/var/www/html/api/vendor/mockery/mockery/library/Mockery/ReceivedMethodCalls.php:46
/var/www/html/api/vendor/mockery/mockery/library/Mockery/VerificationDirector.php:36
/var/www/html/api/vendor/mockery/mockery/library/Mockery/HigherOrderMessage.php:46
/var/www/html/api/tests/Services/MyServiceTest.php:23
phpvfscomposer:///var/www/html/api/vendor/phpunit/phpunit/phpunit:60
Do you know why that does happen?
There is a typo in:
$mockedAnotherService->shouldHaveReceived()->setXY(5,10);
Should be:
$mockedAnotherService->shouldHaveReceived()->SetXY(5,10);

How to correctly extend and use other interfaces?

I'm trying to make use of a base interface for all my other interfaces as follows:
Base interface
<?php
namespace App\Repositories\Data;
interface IDataRepository
{
public function getAll();
public function getById($id);
public function create($model);
public function update($model);
public function delete($id);
}
Implemented base interface
<?php namespace App\Repositories\Data;
use Illuminate\Database\Eloquent\Model;
class DataRepository implements IDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
The interface where i'm trying to make use of the base interface
<?php
namespace App\Repositories;
use App\Repositories\Data\IDataRepository;
interface ITestRepository extends IDataRepository
{
}
implementation
<?php namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
}
In my controller i'm trying to just call test repository so i can use all the base repository functions:
class TestController extends Controller
{
protected $testRepository;
public function __construct(Test $test)
{
$this->testRepository = new TestRepository($test);
}
public function index()
{
$data['testData'] = $this->testRepository->getAll();
return view('test', $data);
}
}
But i get the following error:
Class App\Repositories\TestRepository contains 5 abstract methods and
must therefore be declared abstract or implement the remaining methods
My application works fine if i only make use of my base interface and pass through a model. What would be the correct way to share functions from my base interface across all my other interfaces, so as to prevent code duplication? I appreciate any help.
I think that a Trait which will contains all methods of your interface declaration is the best choice. Something like (not sure about logic):
namespace App\Repositories;
trait TDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
And then just use it for classes with base interface:
namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
use TDataRepository;
}
Also there are some other options:
abstract class with methods for base interface but it not so flexible like trait,
composition but you should change base idea and create a new entity for composition.
<?php
namespace App\Repositories;
use App\Interfaces\ITestRepository;
class TestRepository implements ITestRepository
{
public function getAll()
{
// TODO: Implement getAll() method.
}
public function getById($id)
{
// TODO: Implement getById() method.
}
public function create($model)
{
// TODO: Implement create() method.
}
public function update($model)
{
// TODO: Implement update() method.
}
public function delete($id)
{
// TODO: Implement delete() method.
}
}
Class must be declared abstract or implement methods 'getAll', 'getById', 'update', 'create', 'delete'
So All the method is by default abstract method in interface and you have to define all method in this class.
The class TestRepository should not implement any interface, but extend DataRepository:
<?php namespace App\Repositories;
use App\Repositories\Data\DataRepository;
class TestRepository extends DataRepository
{
}
DataRepository contains already the implementation of the interface IDataRepository. When you create a class implementing ITestRepository you will have to define the implementation of all the methods in the interface (which are the same as the base interface, in your case).

laravel parent constructor does not called child constructor

I am using laravel 5.2,
I have created admin controller and added logic to check admin role in constructor
namespace App\Http\Controllers;
use Sentinel;
class AdminController extends Controller
{
public function __construct()
{
if(Sentinel::check())
{
if(!Sentinel::inRole('admin'))
{
return redirect("login");
}
}
else
{
return redirect("login");
}
}
}
and I extends this controller on some admin controller
namespace App\Http\Controllers;
use Request;
use App\Http\Controllers\AdminController;
use App\Http\Requests;
use Sentinel;
use App\User;
use DB;
class UserController extends AdminController
{
function __construct()
{
parent::__construct();
}
}
When I call user controller admin constructor is called but return function is not working properly, if I add die; before return it get die, but after return notthing is affected.
so it doesn't return redirect function properly.
The ugly workaround would be to pass a boolean param to Papa indicating that you do not wish to parse the code contained in it's constructor. i.e:
// main class that everything inherits
class Grandpa extends Controller
{
public function __construct()
{
}
}
class Papa extends Grandpa
{
public function __construct($bypass = false)
{
// only perform actions inside if not bypassing
if (!$bypass) {
}
// call Grandpa's constructor
parent::__construct();
}
}
class Kiddo extends Papa
{
public function __construct()
{
$bypassPapa = true;
parent::__construct($bypassPapa);
}
}

How to register a default Observer for every Model instance with Laravel

Laravel 5.1
I'm trying to register a single model observer for every Model that extends my AbstractModel (who are extending Illuminate\Database\Eloquent\Model).
The problem is my GenericModelObserver can't listen to events fired by Models inheriting AbstractModel.
Let me show what I did so far.
A Service Provider was created and put on the last position of the providers array inside config/app.php
<?php
// app/Providers/ObserverServiceProvider.php
namespace App\Providers;
use App\Models\Quotation;
use App\Models\AbstractModel;
use App\Observers\QuotationObserver;
use App\Observers\GenericModelObserver;
use Illuminate\Support\ServiceProvider;
class ObserverServiceProvider extends ServiceProvider
{
public function boot()
{
AbstractModel::observe(GenericModelObserver::class);
Quotation::observe(QuotationObserver::class);
}
public function register()
{
}
}
Then I have my plain simple GenericModelObserver
<?php
// app/Observers/GenericModelObserver.php
namespace App\Observers;
use App\Models\AbstractModel;
class GenericModelObserver
{
public function saving(AbstractModel $model)
{
return $model->valid();
}
}
The Abstract Model
<?php
// app/Models/AbstractModel.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AbstractModel extends Model
{
// ...
}
My Quotation Model
<?php
// app/Models/Quotation.php
namespace App\Models;
class Quotation extends AbstractModel
{
// ...
}
When Quotation is saved, the GenericModelObserver can't listen to the saving event or any other event.
The same applies for other Models that don't have a specific Model Observer.
Is this the right strategy? I would not like to bind a observer to every model through the boot method.
Instead of extending model - write your own trait which will work as observer.
Below I wrote some basic trait:
<?php
namespace App\YourPackage\Traits;
use Illuminate\Database\Eloquent\Model;
trait Observable
{
public static function bootObservable()
{
static::updating(function (Model $model) {
dd('updating');
});
}
}
and use it by typing use Observable; in your model class.
Also for your learning take a note how traits is booting: You have to put boot[TraitClassName] method into trait, to boot it properly.
Never write boot method inside your trait, it's dangerous!
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Observers\TeamObserver;
class Team extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
self::observe(TeamObserver::class);
}
}
Why not simply extend a parent class say BaseObserver
I have something similar in my caching system
<?php namespace App\Observers;
class BaseObserver {
public function saving($model)
{
//do your thing here that apply to all observers, like caching
}
}
Then in your Observers
<?php namespace App\Observers;
class Quotation extends BaseObserver{
//you can override any of the methods if you wish
}
Update the boot method in your AppServiceProvider to the following:
public function boot()
{
# Register all model observers
$filesInFolder = \File::files(app_path('/Observers'));
foreach($filesInFolder as $path) {
$observerClassName = pathinfo($path)['filename'];
$className = str_replace('Observer', '', $observerClassName);
$observerClassName = 'App\\Observers\\' . $observerClassName;
$className = 'App\\' . $className;
$className::observe($observerClassName);
}
}
Models should follow this format:
App\User
Observers should follow this format:
App\Observers\UserObserver
When models are in the 'models' folder:
Swap this $className = 'App\\' . $className; for this $className = 'App\\Models\\' . $className;
In your parent model you can do something like that
/**
* If true will attach the observers of the parent class
* #var bool
*/
protected $shouldAttachParentObservers = true;
public static function boot()
{
$instance = new static;
$instance->attachParentObservers();
parent::boot();
}
public function attachParentObservers() {
$parentClass = get_parent_class($this);
if(!empty($parentClass) && $this->shouldAttachParentObservers) {
$eventObservers = [];
foreach ($this->getObservableEvents() as $event) {
$eventObservers[$event] = ($this::$dispatcher->getListeners("eloquent.{$event}: {$parentClass}"));
foreach ($eventObservers[$event] as $observer) {
$eventName = "eloquent.{$event}: {$this::getClassName()}";
$this::$dispatcher->listen($eventName, $observer);
}
}
}
}
/**
* You may use different way to find the class name
*/
public static function getClassName() {
return static::class;
}

How to call a 'parent' trait method in PHP?

This is my code
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller {
use ResetsPasswords;
public function postReset(Request $request){
// do some stuff
// ...
return parent::postReset($request); // <-here is the problem
}
The method postReset is present in ResetsPasswords, but the code I've written is looking for this method within the Controller class.
Any ideas?
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller {
use ResetsPasswords {
ResetsPassword::postReset as traitPostReset;
};
public function postReset(Request $request){
// do some stuff
// ...
return $this->traitPostReset($request);
}
}
The reason why parent::postReset($request) issues a fatal error is because its parent Controller class hasn't postReset method. The trait isn't considered as a parent in that case even if it has an implementation of postReset() method.
To work-around this, you can give it a different name:
class PasswordController extends Controller
{
use ResetsPasswords;
public function postResetPassword(Request $request) // <- giving it a different name
{
// do some stuff
// ...
return $this->postReset($request); // and calling ResetsPasswords::postReset
}

Categories