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
Related
Okay there are questions about the same topic before but they don't help to fully understand this topic
SO SuggestionFirst
SO Suggestion Second
All the code is just to illustrate the situation, So this is the structure
A helper function which does something
namespace App\Helpers;
class Pets{
public function limit($string,$limit,$start = 0){
return substr($string,$start,$limit);
}
}
Now in order to use this helper, since it's a class so i need to create an object like this
CODE SAMPLE FIRST
namespace App\Objects;
use App\Helpers\Pets;
class User{
public function getShortUserName(){
$name = auth()->user()->first_name.' '.auth()->user()->last_name;
$pet = new Pets;
return $pet->limit($name,10);
}
}
But somewhere I got to know that if you add Facades before your namespace, you can call the function statically even if they are non static function like this
CODE SAMPLE SECOND
namespace App\Objects;
use Facades\App\Helpers\Pets;
class User{
public function getShortUserName(){
$name = auth()->user()->first_name.' '.auth()->user()->last_name;
return Pets::limit($name,10);
}
}
Now what I want to know is I have 2 sample codes with namespace as follows
use App\Helpers\Pets;
use Facades\App\Helpers\Pets;
By adding the Facades I can call the function statically but how, that's not a valida namespace in my app
What laravel doing behind the scene, I am so confused
Thank you for your time ;)
What you are describing is Laravels Real-Time Facades.
You can find documentation of the functionality here:
https://laravel.com/docs/6.x/facades#real-time-facades
I will not enter too much in details but this is a simple explanation of what's behind the scenes when you use facades in laravel.
Let's suppose you define a custom class with some public methods:
namespace Test;
class Foo
{
public function test()
{
return 'test';
}
}
Then you have to define a facade for this class:
namespace Test1;
class BarFacade
{
// In laravel this is called in the Facade abstract class but it is actually implemented
// by all the facades you add across the application
public static function getFacadeAccessor()
{
// In laravel you can also return a string which means that the object
// will be retrieved from the container.
return new \Test\Foo();
}
// In laravel this method is defined in the Facade abstract class
public static function __callStatic($method, $args)
{
$object = self::getFacadeAccessor();
return call_user_func_array([$object, $method], $args);
}
}
Then, you have to define the alias in the $aliases array of the config.app file. These aliases are parsed by laravel and registered using the php built-in function class_alias (see Illuminate/Foundation/AliasLoader.php)
class_alias('Test\Foo', 'BarFacade', true);
// You can also create an alias for the facade itself
class_alias('Test1\BarFacade', 'FooBar', true);
Then you can simply call the facades:\
var_dump(BarFacade::test());
var_dump(\Test1\BarFacade::test());
var_dump(\FooBar::test());
The results would obviously be:
string(4) "test"
string(4) "test"
string(4) "test"
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');
}
}
I'm building an application, now i'm created a helper
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
now i'm unable to do something like this in controller
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function index()
{
return view('student/homepage');
}
public function StudentData($first_name = null)
{
/* ********** unable to perform this action *********/
$students = Student::return_student_names();
/* ********** unable to perform this action *********/
}
}
this is my helper service provider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
foreach(glob(app_path().'/Helpers/*.php') as $filename){
require_once($filename);
}
}
}
i event added it as an alias in config/app.php file
'Student' => App\Helpers\Students::class,
Try putting use App\Helpers\Student; at the top of your controller beneath the namespace delcaration:
namespace App\Http\Controllers;
use App\Helpers\Student;
class WelcomeController extends Controller
{
// ...
Look more into PHP namespaces and how they are used, I believe you may have a deficient understanding about them. Their only purpose is to make so you can name and use two classes with the same name (e.g. App\Helpers\Student vs maybe App\Models\Student). If you needed to use both of those classes inside of the same source file, you can alias one of them like this:
use App\Helpers\Student;
use App\Models\Student as StudentModel;
// Will create an instance of App\Helpers\Student
$student = new Student();
// Will create an instance of App\Models\Student
$student2 = new StudentModel();
You do not need to have a service provider for this, just the normal language features. What you would need a service provider for is if you wanted to defer the construction of your Student object to the IoC:
public function register()
{
$app->bind('App\Helpers\Student', function() {
return new \App\Helpers\Student;
});
}
// ...
$student = app()->make('App\Helpers\Student');
You should never have to include or require a class file in laravel because that is one of the functions that composer provides.
You do not need a service provider to make it works. Just lets the Students class as you did:
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
all its methods should be static
You added the Facade correctly:
'Student' => App\Helpers\Students::class,
Finally, looks like your problem is caused by forgetting a backslash at facade name. Uses \Students instead of Students:
public function StudentData($first_name = null)
{
$students = \Student::return_student_names();
}
When using a facade, it is not necessary makes nay include, the facades were made to avoid complex includes in everywhere.
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.
I use Cakephp 2.1 and I need to call a component method which resides in a plugin, from a view helper:
The component is here:
/app/Plugin/Abc/Controller/Component/AbcComponent.php
The helper is here:
/app/View/Helper/SimpleHelper.php
I tried inside helper:
App::import('Component', 'Abc.Abc');
$this->Abc = new Abc(); or $this->Abc = new AbcComponent;
or
$this->Abc = $this->Components->load('Abc.Abc');
inside the controllers this component works with no problem.
I know this isn't recommended (MVC design etc.) but if I don't use it this way I need to duplicate a lot of code. I need to make something like:
MyHelper extends Helper{
$simpleVar = Component->get_data();
}
I use CakePHP 2.4
This is how I successfully call Component from a Helper:
App::uses('AclComponent', 'Controller/Component');
class MyHelper extends AppHelper {
public function myFunction() {
$collection = new ComponentCollection();
$acl = new AclComponent($collection);
// From here you can use AclComponent in $acl
if ($acl->check($aro, $aco) {
// ...
}
}
}
Passing data from CakePHP component to a helper
This seems to be a very nice way to handle this.
I tried working the way you are before, and, although it seems to be a nice immediate solution, in the long run, it is better to just work with the component and helper as 2 separate entities in your controller.
lee
You can put logic in trait and use this from component and helper, if your porpouse is to use the same business logic in different places, to avoid duplication code.
By example
the trait (file app/Lib/NameOfTrait.php or app/PluginName/Lib/NameOfTrait.php)
trait NameOfTrait {
public function theTraitFunc($a, $b) {
// Code here
}
}
The Component:
App::uses('Component', 'Controller');
App::uses('NameOfTrait', 'PluginName.Lib');
class NameOfComponent extends Component {
use NameOfTrait;
private $member;
private $controller;
public function __construct(ComponentCollection $collection, $settings = array()) {
parent::__construct($collection, $settings);
$this->member = $settings['memberName'];
}
function startup(Controller $controller) {
$this->controller = $controller;
}
/**
* Wrap function call of trait function,
* I think the function doesn't have the same name,
* I don't try this but I think is obvious,
* to avoid the function to call itself
*/
public function theTraitFuncWrap($a) {
return $this->theTraitFunc($a, $this->member);
}
}
Do The same for the Helper.
I hope this help someone, bye :)