ReflectionException: Class StripeBilling does not exist - php

I am trying to implement a Billing interface which uses Stripe.
I have created the Billing interface, Stripe class and binded the interface using a Service Provider.
I receive a Class not found error when trying to run the code:
ReflectionException in Container.php line 737: Class
Acme\Billing\StripeBilling does not exist
I can't figure out what the issue is, I've double checked for small issues like correct case etc.
Here is the code I've used:
app/Acme/Billing/BillingInterface.php
<?php
namespace Acme\Billing;
interface BillingInterface {
public function charge(array $data);
}
app/Acme/Billing/StripeBilling.php
<?php
namespace Acme\Billing;
use Stripe;
use Stripe_Charge;
use Stripe_Customer;
use Stripe_InvalidRequestError;
use Stripe_CardError;
use Exception;
class StripeBilling implements BillingInterface {
public function __construct()
{
Stripe::setApiKey(env('STRIPE_SECRET_KEY'))
}
public function charge(array $data)
{
try
{
return Stripe_Charge::create([
'amount' => 1000, // £10
'currency' => 'gbp',
'description' => $data['email'],
'card' => $data['token']
]);
}
catch(Stripe_CardError $e)
{
dd('card was declined');
}
}
}
app/Providers/BillingServiceProvider.php (UPDATED)
class BillingServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Billing\BillingInterface', 'Billing\StripeBilling');
}
}
BasketController.php (ADDED)
public function store(Request $request)
{
$billing = \App::make('Billing\BillingInterface');
return $billing->charge([
'email' => $request->email,
'stripe-token' => $request->token,
]);
I have added App\Providers\BillingServiceProvider::class to my app.php file, and updated my composer.json to include Acme folder "Acme\\": "app/"

Your problem looks two-fold:
The PSR-4 autoload definition in your composer.json file is incorrect.
If your Acme folder lives inside the app folder, e.g. /dir/project_root/app/Acme/Billing/BillingInterface.php, then your composer.json definition should look like this:
"psr-4": {
"Acme\\": "app/Acme"
}
This is the root cause of the error you are receiving, which is not a Laravel-specific error. The autoloader simply cannot find the class you are asking for, even though the requested fully qualified class name is correct.
Your interface and class are not bound to the container properly (missing the Acme portion of the namespace).
Because you've defined both of them in the Acme namespace, you need to ensure Acme is present in your service provider definitions. So your service provider should look like this:
class BillingServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Billing\BillingInterface', 'Acme\Billing\StripeBilling');
}
}
(Or, better yet, use the ::class syntax for improved IDE support.)
You will also need to make sure the fully qualified classname is correct when requesting the class in your controller: App::make('Acme\Billing\BillingInterface'). (I would recommend using dependency injection instead of this syntax, anyway.)

Related

Why's my interface not instantiable even though I have what appears to be the correct files/implementation?

I'm not sure why I'm getting this error that says Illuminate\Contracts\Container\BindingResolutionException: Target [App\Dal\Interfaces\IUploadsRepository] is not instantiable while building [App\Http\Controllers\FileUploadController]. in file /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 1093 despite having (to my knowledge) everything set up correctly. The spelling and everything else is correct but I'm still not sure what the issue is.
I've tried everything under the sun to make this work but I'm not sure what I'm doing wrong.
What am I missing?
Note: I need to declare the UploadsRepository.php class as abstract because if I don't, then I get a red squiggly line underneath the class name with a warning that says:
Class must be declared abstract or implement methods 'resetScope', 'hidden', 'syncWithoutDetaching', 'update', 'paginate', 'delete', 'findWhereBetween', 'whereHas', 'withCount', 'find', 'getFieldsSearchable', 'create', 'findWhereNotIn', 'setPresenter', 'skipPresenter', 'all', '__callStatic', 'findWhere', 'visible', 'simplePaginate', 'firstOrNew', 'orderBy', 'sync', 'scopeQuery', 'findWhereIn', 'findByField', 'with', 'lists', 'firstOrCreate', 'updateOrCreate', '__call', 'pluck'
I'm not sure if this is the root of the issue but just want to provide as much info as I possibly can.
Here's FileUploadController.php:
<?php
namespace App\Http\Controllers;
use App\Dal\Interfaces\IUploadsRepository;
Use App\Dal\Repositories\UploadsRepository;
class FileUploadController extends Controller
{
protected $__uploadsRepository;
public function __construct(IUploadsRepository $uploadsRepository)
{
$this->__uploadsRepository = $uploadsRepository;
}
public function getUploads(): string
{
return $this->__uploadsRepository->getUploads();
}
}
Here's IUploadsRepository.php (interface):
<?php
namespace App\Dal\Interfaces;
use Prettus\Repository\Contracts\RepositoryInterface;
interface IUploadsRepository extends RepositoryInterface
{
public function getUploads();
}
Here's UploadsRepository.php:
<?php
namespace App\Dal\Repositories;
use App\Dal\Interfaces\IUploadsRepository;
abstract class UploadsRepository implements IUploadsRepository
{
/**
* #return string
*/
public function getUploads(): string
{
return "test";
}
}
Here's RepositoryServiceProvider.php:
<?php
namespace App\Providers;
use App\Dal\Interfaces\IUploadsRepository;
use App\Dal\Repositories\UploadsRepository;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
public function register() {
$this->app->bind(IUploadsRepository::class,UploadsRepository::class);
}
}
Here's config/app.php:
'providers' => [
Prettus\Repository\Providers\RepositoryServiceProvider::class,
\App\Providers\RepositoryServiceProvider::class,
]
The RepositoryInterface you are extending defines all those methods you see in the error: "'resetScope', 'hidden', 'syncWithoutDetaching', 'update'...". Your concrete implementation UploadsRepository is only implementing getUploads(). To fulfill the contract defined by the RepositoryInterface your concrete implementation needs to implement also the other methods. My recommendation would be to rather than implementing that interface, have your UploadsRepository extends from the Prettus\Repository\Eloquent\BaseRepository class which offers a default implementation for those methods. You can declare like this.
First IUploadsRepository does not need to extend RepositoryInterface, so the declaration would be:
<?php
namespace App\Dal\Interfaces;
interface IUploadsRepository
{
public function getUploads();
}
And the concrete implementation then goes like this:
<?php
namespace App\Dal\Repositories;
use App\Dal\Interfaces\IUploadsRepository;
use Prettus\Repository\Eloquent\BaseRepository
class UploadsRepository extends BaseRepository implements IUploadsRepository
{
/**
* #return string
*/
public function getUploads(): string
{
return "test";
}
}
You now have a default implementation for the missing methods and can use IUploadsRepository interface to handle the dependency injection via the RepositoryServiceProvider.

Use Plates PHP with dependency injection

I want to use dependency injection to pass an instance of Plates to my controllers with PHP-DI that is integrated with my routing system Simple Router.
I've tried to inject an instance of Plates, but I get this error:
<?php
namespace Controllers;
use \League\Plates\Engine;
use \League\Plates\Template\Template;
use \League\Plates\Extension\Asset;
class Controller {
public function __construct(\League\Plates\Engine $templates)
{
$this->templates = $templates;
}
?>
Uncaught LogicException: The template name "home" is not valid. The default directory has not been defined
How I can solve this issue? I need also to pass the assets path with the asset() method. Any help will be appreciated.
UPDATE
Thanks to the help of jcHache I've managed the injection of a Plates instance inside my base controller with this DI code:
<?php
// config.php
return [
League\Plates\Engine::class => DI\create()
->constructor(TEMPLATE_ROOT)
->method('loadExtension', DI\get('League\Plates\Extension\Asset')),
League\Plates\Extension\Asset::class => DI\create()
->constructor(APP_ROOT),
];
index.php file
<?php
use Pecee\SimpleRouter\SimpleRouter;
use DI\ContainerBuilder;
$container = (new \DI\ContainerBuilder())
->useAutowiring(true)
->addDefinitions('config.php')
->build();
SimpleRouter::enableDependencyInjection($container);
This is great but I'm facing a problem and I can't find a fix for it.
I get this error that is relative to the assets loader of plates, it seems that it's instantiated more than once. I've extended my controllers with my base controller where the asset loader is instantiated, but I don't think is this the problem? Is there a fix?
Uncaught Pecee\SimpleRouter\Exceptions\NotFoundHttpException: The template function name "asset" is already registered
Plates engine factory require a view folder parameter (see Plates doc):
so you have to add this creation in your PHP-DI configuration file:
For Plates V4:
// config.php
return [
// ...
\League\Plates\Engine::class => function(){
return League\Plates\Engine::create('/path/to/templates', 'phtml');
},
];
For Plates V3, I'll try:
// config.php
return [
// ...
\League\Plates\Engine::class => function(){
return new League\Plates\Engine('/path/to/templates');
},
];
or
// config.php
return [
// ...
\League\Plates\Engine::class => DI\create()
->constructor('/path/to/templates')
,
];
Design Note:
Personally, I won't use dependency injection for a template engine, I think it would be better to instantiate Plates engine in a base controller class.
namespace controllers;
use League\Plates\Engine;
abstract class BaseController
{
/**
* #var \League\Plates\Engine
*/
protected $templates;
public function __construct()
{
$this->templates=new Engine(\TEMPLATE_ROOT);
$this->templates->loadExtension(new \League\Plates\Extension\Asset(\APP_ROOT));
}
protected function renderView(string $viewname, array $variables=[])
{
return $this->templates->render($viewname,$variables);
}
}
For a child controller using Plates:
namespace controllers;
class MyController extends BaseController
{
public function index()
{
return $this->renderView('home');
}
}

Why isn't the "user" class found?

I'm building a PHP web application with Laravel and am trying to pass a name and email from a form to my database, but it displays this error:
Fatal error: Uncaught Error: Class 'user' not found in
C:\xampp\htdocs\MVC\app\controller\home.php:20 Stack trace: #0
C:\xampp\htdocs\MVC\app\core\app.php(43): home->create('hala',
'hala#yahoo') #1 C:\xampp\htdocs\MVC\public\index.php(4):
app->__construct() #2 {main} thrown in
C:\xampp\htdocs\MVC\app\controller\home.php on line 20
This is the code I'm using for the home page:
class home extends controller
{
public function index($name = '')
{
$this->view('home/index', ['name'=>$user->name]);
}
public function create($username = '', $email = '')
{
user::create([
'username' => $username,
'email'=> $email
]);
}
}
and the model:
use Illuminate\Database\Eloquent\Model as Eloquent;
class user extends Eloquent
{
public $name;
protected $fillable = ['username','email'];
}
What am I doing wrong and how can I fix it?
In your controller code, you need to include the user class:
require_once("user.class.php"); /* or whatever the file is named */
If this is done automatically and the class is in a different namespace, you need to declare your intent to use it in the controller:
use \my\namespace\user;
Or just use the fully qualified class name in your code:
\my\namespace\user::create();
If you use illuminate/database then chances are you are using composer. Why not add a PSR-4 auto load rule and structure your code accordingly. Eg. composer.json might look like this:
{
"name": "acme/acme",
"description": "Acme is this and that",
"type": "project",
"require": {
"php": "^7.2",
"illuminate/database": "^5.7"
},
"autoload": {
"psr-4": {
"Acme\\": "src"
}
},
"license": "proprietary"
}
Runing composer install makes you an vendor/autoloader.php and it is the only file you need to require. You put your own code un the Acme (or whatever you chose) namespace. Eg. You put your user model under src/Model/User.php and add your namespace:
<?php
namespace Acme\Model;
use Illuminate\Database\Eloquent\Model as Eloquent;
class User extends Eloquent
{
public $name;
protected $fillable = ['username','email'];
}
Your main file might look like this..
<?php
// entry point file
require_once('vendor/autoload.php');
use Acme\Model\User;
$user = new User();
// ...
Obviously you would make most logic in some class so this should be quite short.
This might seem obvious to people working on recent projects, but I have seen too many projects that still have a static file including all the classes like we did in the olden days. Move your projects to the 2010s now!

Laravel Interfaces

I used the following tutorial to get an idea about interfaces:
http://vegibit.com/what-is-a-laravel-interface/
But I wanted to change the directory of where I am putting my interfaces to "App/Models/Interfaces". And so I did. But now I cannot get it to work anymore. Here is my code:
Routes.php
App::bind('CarInterface', 'Subaru');
Route::get('subaru', function()
{
$car = App::make('CarInterface');
$car->start();
$car->gas();
$car->brake();
});
Model Subaru.php
<?php
use App\Models\Interfaces\CarInterface;
class Subaru implements CarInterface {
..etc
Interface CarInterface
<?php namespace App\Models\Interfaces;
interface CarInterface {
public function start();
public function gas();
public function brake();
}
I added this in my composer.json:
"psr-0": {
"Interfaces": "app/models/interfaces"
}
And I even added this in my start/global.php file:
ClassLoader::addDirectories(array(
app_path().'/models/interfaces',
In my recent laravel 5 project, I'm used to prepare my logics as Repository method.
So here's my current directory structure. For example we have 'Car'.
So first I just create directory call it libs under app directory and loaded it to composer.json
"autoload": {
"classmap": [
"database",
"app/libs" //this is the new changes (remove this comment)
]
}
after that I create a subfolder call it Car . Under the Car folder create two file 'CarEloquent.php' for eloquent implementation and CarInterface.php as interface.
CarInterface
namespace App\libs\Car;
interface CarInterface {
public function getAll();
public function create(array $data);
public function delete($id);
public function getByID($id);
public function update($id,array $data);
}
CarEloquent
namespace App\lib\Car;
use App\lib\Car\CarInterface;
use App\Car; //car model
class CarEloquent implements CarInterface {
protected $car;
function __construct(Car $a) {
$this->car = $a;
}
public function getAll(){
return $this->car->all();
}
}
Then create Car Service Provider to bind ioc controller.
For create Car service provider you can also use php artisan command by laravel.
php artisan make:provider CarServiceProvider
ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class CarServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind('App\lib\Car\CarInterface', 'App\lib\Car\CarEloquent');
}
}
And final step would be add these service provider to config/app.php provider array.
'providers' => [
'App\Providers\CatServiceProvider',
]
And finally we are ready to use our repository method in our controller.
Example Controller
namespace App\Http\Controllers;
use App\lib\Car\CarInterface as Car;
class CarController extends Controller {
protected $carObject;
public function __construct(Car $c) {
$this->carObject = $c;
}
public function getIndex(){
$cars = $this->carObject->getAll();
return view('cars.index')->with('cars',$cars);
}
}
Main purpose to achieve here call repository method to controller, however you need use them as per your requirement.
Update
CarEloqent basically help us to improve database implementation, for example in future if you want to implement same functionality for other database like redis you just add another class CarRedis and change implementation file path from server provider.
Update 1: Good Resource
http://programmingarehard.com/2014/03/12/what-to-return-from-repositories.html
[book] From Apprentice to Artisan by Taylor Otwell
Very good explanation about repository method and software design principle commonly called separation of concerns. You should read this book.
If you still have any confusion to achieve these behaviors let me know and however I will keep eye on this question to update this answer, if I find some things to change or update or as per requirement.

Laravel error: Target [Illuminate\View\Engines\EngineInterface] is not instantiable

I'm creating an abstract class that will grab the contents of a view using Laravel's View class. But I'm getting the following error when trying to run a method from a class that extends it:
Illuminate \ Container \ BindingResolutionException
Target [Illuminate\View\Engines\EngineInterface] is not instantiable.
Here's my code:
PdfReport.php
use Illuminate\View\View as View;
abstract class PdfReport {
private $view;
function __construct(View $view)
{
$this->view = $view;
}
public function render($reportView, $report)
{
$this->view->make('report.pdf.' . $reportView, ['report' => $report])->render();
}
}
EslReport.php
<?php namespace Reports\PdfReports;
class EslPdfReport extends PdfReport {
public function renderReport($report)
{
return $this->render('esl', $report);
}
}
Then I'm running my code in routes.php for testing purposes as follows:
use Reports\PdfReports\EslPdfReport;
Route::get('pdftest', array(
'as' => 'pdftest',
function(){
$eslReport = App::make('Reports\PdfReports\EslPdfReport');
$eslReport->renderReport(EslReport::find(1));
}
));
I'm not quite understanding if I'm doing something wrong with the dependency injection for the view in the abstract class, it's all pretty new concepts to me, so any help would be most appreciated.
Also I asked this question on laracasts forum if it helps: https://laracasts.com/discuss/channels/general-discussion/confusion-about-constructors-in-abstract-classes
Instead of Illuminate\View\View you need to inject Illuminate\View\Factory:
use Illuminate\View\Factory as View;
Here's a reference of facade classes and there actual underlying class you need to use when working with DI

Categories