I know that this question was asked so many times, but none of answers helped me.
I'm getting exception in Laravel 5.8:
Target [App\Repositories\Category\CategoryRepositoryInterface] is not instantiable while building [App\Http\Controllers\Admin\CategoryController, App\Services\CategoryServices].
What I've done without success:
Register App\Providers\RepositoryServiceProvider in providers
Structure:
app
- Repositories
- CategoryRepository.php
- CategoryRepositoryInterface.php
- Models
- CategoryModel.php
- Providers
- RepositoryServiceProvider.php
- Services
- CategoryService.php
CategoryRepositoryInterface.php:
<?php
namespace App\Repositories\Category;
interface CategoryRepositoryInterface
{
public function show();
}
CategoryRepository.php:
<?php
namespace App\Repositories\Category;
use App\Models\CategoryModel;
// use Illuminate\Support\Facades\DB;
use App\Repositories\Category\CategoryRepositoryInterface;
class CategoryRepository implements CategoryRepositoryInterface
{
const TABLE = 'category';
private $model;
public function __construct()
{
$this->model = new CategoryModel();
}
public function show()
{
return $this->model->all();
}
}
RepositoryServiceProvider.php:
<?php
namespace App\Providers;
use App\Repositories\Category\CategoryRepository;
use App\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
public function boot() {}
public function register()
{
$this->app->singleton(CategoryRepositoryInterface::class, CategoryRepository::class);
}
}
CategoryController.php:
class CategoryController extends Controller
{
private $categoryServices;
function __construct(categoryServices $categoryServices)
{
$this->categoryServices = $categoryServices;
// dd($categoryServices);
}
public function index()
{
try {
$category = $this->categoryServices->index();
return view('AdminStore.pages.categorys.cate_list', compact('category'));
} catch (\Exception $e) {
abort('500');
}
}
composer.json:
"autoload": {
"psr-4": {
"App\\": "app/",
"App\\Models\\": "app/Models/",
"App\\Repositories\\": "app/Repositories/"
},
Related
I'm doing some tests on prestashop 1.7.7.0.rc1 and trying to create an administration controller with Symfony on a module.
I encounter a ClassNotFoundException with the FrameworkBundleAdminController
// ht_doctrine.php
declare(strict_types=1);
if (!defined('_PS_VERSION_')) {
exit;
}
if (file_exists(__DIR__.'/vendor/autoload.php')) {
require_once __DIR__.'/vendor/autoload.php';
}
class Ht_doctrine extends Module
{
public function __construct()
{
$this->name = 'ht_doctrine';
$this->author = 'Hugues';
$this->version = '1.0.0';
$this->ps_versions_compliancy = ['min' => '1.7.7', 'max' => _PS_VERSION_];
$this->need_instance = 0;
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('Exemple HW Doctrine');
$this->description = $this->l('Demonstration des entités Doctrine');
}
public function install()
{
return parent::install()
&& $this->registerHook('displayHome');
}
public function uninstall()
{
return parent::uninstall();
}
public function getContent()
{
Tools::redirectAdmin(
$this->context->link->getAdminLink('AdminHtDoctrineQuote')
);
}
public function hookDisplayHome()
{
$this->smarty->assign(['quotes' => []]);
return $this->fetch('module:ht_doctrine/views/templates/hooks/quotes.tpl');
}
}
# config/routes.yml
htdoctrine_quote_index:
path: /htdoctrine/quotes
methods: [GET]
defaults:
_controller: 'Prestashop\Module\HtDoctrine\Controller\Admin\QuotesController::indexAction'
_legacy_controller: 'AdminHtDoctrineQuote'
_legacy_link: 'AdminHtDoctrineQuote'
// src/Controller/Admin/QuotesController.php
<?php
namespace Prestashop\Module\HtDoctrine\Controller\Admin;
use PrestashopBundle\Controller\Admin\FrameworkBundleAdminController;
use Symfony\Component\HttpFoundation\Response;
class QuotesController extends FrameworkBundleAdminController
{
public function indexAction(): Response
{
return $this->render('#Modules/ht_doctrine/views/templates/admin/index.html.twig');
}
}
composer.json :
{
"name": "lhapaipai/htdoctrine",
"autoload": {
"psr-4": {
"Prestashop\\Module\\HtDoctrine\\": "src/"
}
},
"config": {
"prepend-autoloader": false,
},
"type": "prestashop-module"
}
before I want to use this module, I write :
rm -rf var/cache/dev
cd modules/ht_doctrine
composer dump-autoload
I try to navigate to my conf page and It's ok ! BUT if I refresh the page, I have an error message from Symfony.
Attempted to load class "FrameworkBundleAdminController" from namespace "PrestashopBundle\Controller\Admin".
Did you forget a "use" statement for "PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController"?
ClassNotFoundException
in modules/ht_doctrine/src/Controller/Admin/QuotesController.php (line 7)
namespace Prestashop\Module\HtDoctrine\Controller\Admin;
use PrestashopBundle\Controller\Admin\FrameworkBundleAdminController;
use Symfony\Component\HttpFoundation\Response;
--> class QuotesController extends FrameworkBundleAdminController
{
public function indexAction(): Response
{
return $this->render('#Modules/ht_doctrine/views/templates/admin/index.html.twig');
}
If I clear the cache it's ok, but for one try.. after I encounter this error again.
do you have any idea what i missed ?
I didn't do any composer action at the root of the project.
I do not understand very well how the composer.json file of the module and the main composer.json file interact with each other.
Thank you for your ideas !
The problem came from a forgotten capital letter in my namespace (PrestaShop)...
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
instead of bad : use PrestashopBundle\Controller\Admin\FrameworkBundleAdminController;
First, I apologize if this is a stupid question. I recently read an article about repository design pattern and I have a problem when making interface implementation for Laravel Query Builder (Illuminate\Support\Facades\DB).
DatabaseService.php
use Modules\Core\Interfaces\IDatabase;
use \DB;
class DatabaseService implements IDatabase
{
protected $db;
public function __construct(DB $db)
{
$this->db = $db;
}
public function select($str)
{
$this->db::select($str);
return $this->db;
}
public function table($tableName)
{
$this->db::table($tableName);
return $this->db;
}
...
}
IDatabase.php
<?php namespace Modules\Core\Interfaces;
interface IDatabase
{
public function select($str);
public function table($tableName);
public function raw($rawQuery);
public function transaction($callback);
public function first();
public function get();
}
CoreServiceProvider.php
...
public function register()
{
...
$this->app->bind('Modules\Core\Interfaces\IDatabase', function($app) {
$db = $app->make(DB::class);
return new DatabaseService($db);
});
...
}
MailboxRepository.php
<?php namespace Modules\Mailbox\Repositories;
use Illuminate\Database\Eloquent\Model;
use Modules\Core\Interfaces\IDatabase;
use Modules\Mailbox\Interfaces\IMailbox;
class MailboxRepository implements IMailbox
{
public function __construct(..., IDatabase $db)
{
...
$this->db = $db;
}
...
public function getBadges()
{
$badges = $this->db->table('mailbox as a')
->select($this->db->raw(
"SUM(a.type = 'inbox') as inbox,
SUM(a.is_read = 0 AND a.type = 'inbox') as unread,
SUM(a.type = 'sent') as sent,
SUM(a.type = 'draft') as draft,
SUM(a.type = 'outbox') as outbox,
SUM(a.type = 'spam') as spam,
SUM(a.type = 'trash') as trash,
SUM(a.is_starred = 1) as starred"
))
->first();
return $badges;
}
...
}
MailboxServiceProvider.php
<?php namespace Modules\Mailbox;
...
use Modules\Mailbox\Interfaces\IMailbox;
use Modules\Mailbox\Repositories\MailboxRepository;
use Modules\Core\Interfaces\IDatabase;
use Illuminate\Support\ServiceProvider;
class MailboxServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->bind(IMailbox::class, function($app) {
return new MailboxRepository(
..., $app->make(IDatabase::class)
);
});
}
public function provides()
{
return [IMailbox::class];
}
}
With error message :
[2018-01-31 13:45:04] local.ERROR: Call to undefined method Illuminate\Support\Facades\DB::select()
{"userId":1,"email":"info#narpandi.com","exception":"[object]
(Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Call
to undefined method Illuminate\\Support\\Facades\\DB::select() at
/var/www/personal-
website/app/Modules/Mailbox/Repositories/MailboxRepository.php:86)
How to do this correctly? Thank you for your kind help.
I don't think this is a common repository pattern, in repository pattern you try to create methods like:
Object get(Object id);
void create(Object entity);
void update(Object entity);
void delete(Object entity);
Edit, try to do something like docs: Database
use Modules\Core\Interfaces\IDatabase;
use Illuminate\Support\Facades\DB;
class DatabaseService implements IDatabase
{
public function select($str, $args)
{
return DB::select($str, $args);
}
}
But i say again this doesn't look like a repository.
After trial and error, I finally figure out how to do this. As mentioned in https://stackoverflow.com/a/26356144/3050636, you cannot use facade DB directly so instead you need to explicitly pass class that is behind the DB facade.
In my case, I use bridge pattern and fluent interface (CMIIW) so I will provide two versions:
Without bridge pattern
MailboxServiceProvider.php
<?php namespace Modules\Mailbox;
use Modules\Mailbox\Interfaces\IMailbox;
...
use Modules\Mailbox\Repositories\MailboxRepository;
use Illuminate\Support\ServiceProvider;
/* Use these instead of DB facade */
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Connectors\ConnectionFactory;
class MailboxServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->bind(IMailbox::class, function($app) {
return new MailboxRepository(
..., new DatabaseManager($app, new ConnectionFactory($app))
);
});
}
public function provides()
{
return [IMailbox::class];
}
}
MailboxRepository.php
<?php namespace Modules\Mailbox\Repositories;
use Illuminate\Database\DatabaseManager;
use Modules\Mailbox\Interfaces\IMailbox;
class MailboxRepository implements IMailbox
{
...
protected $db;
public function __construct(..., DatabaseManager $db)
{
...
$this->db = $db;
}
...
}
With bridge pattern and fluent interface
CoreServiceProvider.php
<?php namespace Modules\Core;
....
use Modules\Core\Services\DatabaseService;
use Modules\Mailbox\Repositories\MailboxRepository;
use Modules\Core\Interfaces\IDatabase;
use Modules\Mailbox\Interfaces\IMailbox;
/* Use these instead of DB facade */
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Connectors\ConnectionFactory;
use Illuminate\Support\ServiceProvider;
class CoreServiceProvider extends ServiceProvider
{
public function register()
{
...
$this->app->bind(IDatabase::class, function($app) {
return new DatabaseService(new DatabaseManager($app, new ConnectionFactory($app)));
});
$this->app->bind(IMailbox::class, function($app) {
return new MailboxRepository(
..., $app->make(IDatabase::class)
);
});
...
}
}
DatabaseService.php
<?php namespace Modules\Core\Services;
use Modules\Core\Interfaces\IDatabase;
use Illuminate\Database\DatabaseManager;
class DatabaseService implements IDatabase
{
protected $db;
public function __construct(DatabaseManager $db)
{
$this->db = $db;
}
public function select($str)
{
$this->db = $this->db->select($str);
return $this->db;
}
public function table($tableName)
{
$this->db = $this->db->table($tableName);
return $this->db;
}
...
}
MailboxRepository.php
<?php namespace Modules\Mailbox\Repositories;
use Modules\Core\Interfaces\IDatabase;
use Modules\Mailbox\Interfaces\IMailbox;
class MailboxRepository implements IMailbox
{
...
protected $db;
public function __construct(..., IDatabase $db)
{
...
$this->db = $db;
}
...
}
I'm building a Lumen app. I'm trying to use interface for my repositories. All my logic is wrapped in a composer package.
Here's my vendor/package/src/app/Providers/PackageServiceProvider.php:
<?php
namespace Vendor\Package\App\Providers;
use Illuminate\Support\ServiceProvider;
class SmsBackendCoreServiceProvider extends ServiceProvider
{
protected $defer = false;
public function register()
{
$this->app->bind(
'Vendor\Package\App\Repositories\Contracts\SmsService',
'Vendor\Package\App\Repositories\Services\SmsJson'
);
}
public function boot()
{
$this->app->group(
['namespace' => 'Vendor\Package\App\Http\Controllers'],
function ($app) {
require __DIR__.'/../../routes/web.php';
}
);
}
}
Here is my vendor/package/src/routes/web.php:
<?php
$app->get('/sms/send.json', 'JsonController#send');
Here is my vendor/package/src/app/Http/Controllers/JsonController.php:
<?php
namespace Vendor\Package\App\Http\Controllers;
use Vendor\Package\App\Http\Controllers\BaseController;
use Vendor\Package\App\Repositories\Contracts\SmsService;
class JsonController extends BaseController
{
public $service;
public function __construct(SmsService $service)
{
$this->service = $service;
}
public function send()
{
$response = $this->service->sendSms(1, 2, 3);
return $response;
}
}
Here is my vendor/package/src/app/Repositories/Contracts/SmsService.php:
<?php
namespace Vendor\Package\App\Repositories\Contracts;
class SmsService
{
public function sendSMS($from, $to, $text);
}
Finally, here is my vendor/package/src/app/Repositories/Services/SmsJson.php:
<?php
namespace Vendor\Package\App\Repositories\Services;
use Vendor\Package\App\Repositories\Contracts\SmsService;
class SmsJson implements SmsService
{
public function sendSMS($from, $to, $text)
{
echo 'success';
}
}
When I try to access http://mydomain.dev/sms/send.json, I get this error:
FatalErrorException in SmsService.php line 7: Non-abstract method
Mitto\SmsBackendCore\App\Repositories\Contracts\SmsService::sendSMS()
must contain body
Where did I go wrong?
Your contract is declared as a class, it must be an interface.
I have a class which acts like a storage (add/get item). I try to bind it as a singleton in one service provider, and resolve it in another's boot method.
The code is changed for simplicity.
app/Providers/BindingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider as ServiceProvider;
class MyBindingFacade extends Facade {
public static function getFacadeAccessor() {
return 'my.binding';
}
}
class MyBinding {
protected $items = [];
public function add($name, $item) {
$this->items[$name] = $item;
}
public function get($name) {
return $this->items[$name];
}
public function getAll() {
return $this->items;
}
}
class BindingProvider extends ServiceProvider {
public function register() {
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
}
public function provides() {
return [
'my.binding',
];
}
}
app/Providers/ResolvingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider as ServiceProvider;
use App\Providers\MyBinding;
class ResolvingProvider extends ServiceProvider {
public function boot(MyBinding $binding) {
$binding->add('foo', 'bar');
// $manual = $this->app->make('my.binding');
// $manual->add('foo', 'bar');
}
public function register() {}
}
app/Http/Controllers/WelcomeController.php
<?php
namespace App\Http\Controllers;
use App\Providers\MyBindingFacade;
class WelcomeController extends Controller {
public function index()
{
dd(MyBindingFacade::getAll()); // debug items
}
}
When I try to debug MyBinding state in my WelcomeController I'm getting empty item array. However, if I uncomment $manual part from my ResolvingProvider it returns an array containing 'foo' => 'bar'. Does it mean IoC resolution is broken in ServiceProvider::boot() method or am I misusing Laravel functionality?
Laravel version: 5.0.28
UPDATE: Added code sample from WelcomeController.
With this:
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
You're saying: my.binding is a singleton and resolves to an instance of App\Providers\MyBinding.
That doesn't mean that App\Providers\MyBinding is registered as singleton too. What you should do instead is this:
$this->app->singleton('App\Providers\MyBinding');
$this->app->bind('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
Because the Facade binding uses $app->make() you should get the same instance you registered with $this->app->singleton() right above.
In the first example you are not using the Facade, you should be using:
use App\Providers\MyBindingFacade as MyBinding;
Which will in fact call make it using 'my.binding'.
My Controller :
<?php
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Admin\Controller;
use Admin\Service;
class AdminController extends AbstractActionController
{
public function indexAction()
{
$CrudService = $this->getServiceLocator()->get('CrudService');
return new ViewModel(
array('list'=> $CrudService->getList())
);
}
}
Service Layer :
<?php
namespace Admin\Service;
use Admin\Dao;
class CrudService
{
public function getList()
{
$CrudDao=new Dao\CrudDao();
$list=$CrudDao->getList();
return $list;
}
}
Dao Layer :
<?php
namespace Admin\Dao;
class CrudDao
{
public function getList()
{
return
$this->getServiceLocator()->
get('doctrine.entitymanager.orm_default')->
getRepository('Admin\Entity\ProductEntity')
->findAll();
}
}
every things is good work But My Problem is Dao Layer. that give me This
Error : not Found get Service Locator Class
I want get data From Doctrine in Dao Layer and Call Dao Method in Service Layer And Next Call Service With getServiceLocator in Controller
You have to inject all dependencies and use the service manager to get the classes.
In you Module.php you have to register and inject the dependencies:
class Module
{
// ...
public function getServiceConfig()
{
$factories = [
'Admin\Dao\CrudDao' = function (ServiceManager $serviceManager) {
$entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'),
return new CrudDao($entityManager);
},
'Admin\Service\CrudService' = function (ServiceManager $serviceManager) {
return new CrudService($serviceManager);
}
];
return $factories;
}
}
The Dao will receive the EntityManager:
<?php
namespace Admin\Dao;
class CrudDao
{
private $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function getList()
{
return
$this->entityManager
->getRepository('Admin\Entity\ProductEntity')
->findAll();
}
}
Your CrudService will receive the Service manager, then you can get the CrudDao:
<?php
namespace Admin\Service;
use Admin\Dao;
class CrudService
{
public function __construct($serviceManager)
{
$this->serviceManager = $serviceManager;
}
public function getList()
{
$CrudDao= $this->serviceManager->get('Admin\Dao\CrudDao');
$list = $CrudDao->getList();
return $list;
}
}
And your controller:
<?php
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Admin\Controller;
use Admin\Service;
class AdminController extends AbstractActionController
{
public function indexAction()
{
$CrudService = $this->getServiceLocator()->get('Admin\Service\CrudService');
return new ViewModel(
array('list'=> $CrudService->getList())
);
}
}