How do I get a variable from an extended controller? - php

I have this base controller:
abstract class ApiController extends BaseController {
use DispatchesCommands, ValidatesRequests;
public function __construct()
{
try {
$user = JWTAuth::parseToken()->toUser();
} catch (Exception $e) {
return Response::json(['error' => $e->getMessage()], HttpResponse::HTTP_UNAUTHORIZED);
}
}
}
How do I get the $user variable in child controllers? Please provide an example child controller if possible.

First of all:
abstract class ApiController extends BaseController {
use DispatchesCommands, ValidatesRequests;
protected $user; // or public
public function __construct()
{
try {
$this->user = JWTAuth::parseToken()->toUser();
} catch (Exception $e) {
return Response::json(['error' => $e->getMessage()], HttpResponse::HTTP_UNAUTHORIZED);
}
}
}
And then:
class Ctrl extends ApiController
{
public function whatever()
{
echo $this->user;
}
}
Last but not least: http://www.phpfreaks.com/tutorial/oo-php-part-1-oop-in-full-effect
Happy coding !

Related

how to create a unique key for caching query in laravel

I used repository in a project that caching all queries.
there's a BaseRepository.
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class BaseRepository implements BaseRepositoryInterface{
protected $model;
protected int $cacheDuration = 600; //per seconds
public function __construct(Model $model)
{
return $this->model = $model;
}
public function paginate(int $paginate,string $cacheKey)
{
return Cache::remember($cacheKey,$this->cacheDuration , function () use ($paginate) {
return $this->model->latest()->paginate($paginate);
});
}
// other methods ...
}
then i used this repository in my service
PostService:
use Illuminate\Support\Facades\App;
class PostService{
public PostRepositoryInterface $postRepository;
public function __construct()
{
$this->postRepository = App::make(PostRepositoryInterface::class);
}
public function paginate(int $paginate, string $cacheKey)
{
return $this->postRepository->paginate($paginate,$cacheKey);
}
}
finally i using the PostService in my controller
PostController:
class PostController extends Controller{
public PostService $postService;
public function __construct()
{
$this->postService = App::make(PostService::class);
}
public function index()
{
string $cacheKey = "posts.paginate";
return $this->postService->paginate(10);
}
}
the index method will return top 10 latest record correctly. now i need to create a unique CacheKey for all Repository queries. for example
TableName concat FunctionName // posts.paginate
so i can use this code into all method of Repository
public function paginate(int $paginate)
{
$cacheKey = $this->model->getTable().__FUNCTION__;
return Cache::remember($cacheKey,$this->cacheDuration , function () use ($paginate) {
return $this->model->latest()->paginate($paginate);
});
}
this is fine. but the problem is that this code repeat in all the method of this class.
if i use this code in another class, method name's will be incorrect.
What do you suggest to prevent duplication of this code?
I solve this problem by passing function name to another class
I created CacheKey class:
class CacheKey{
public static function generate(Model $model, $functionName):string
{
return $model->getTable()."_".$functionName;
}
}
Then in any method of repository we can use this helper class as follows:
$cacheKey = CacheKey::generate($this->model,__FUNCTION__);
you can easily use magic method in this way:
class CacheService {
private const $cacheableMethods = ['paginate'];
private $otherSerivce;
public __construct($otherSerivce) {
$this->otherSerivce = $otherSerivce;
}
public __get($method, $args) {
if(!in_array($method, static::$cachableMethods)) {
return $this->otherSerivce->{$method}(...$args);
}
return Cache::remember(implode([$method, ...$args], ':'), function () {
return $this->otherSerivce->{$method}(...$args);
});
}
}

Laravel 5.8: Interface is not instantiable

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/"
},

Interface with DB Facade in Laravel

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;
}
...
}

build model structure - Fat Free Framework

I'm creating model structure with f3 but towards to end I stuck.
I cant access to my functions another page.
Here is error:
Fatal error: Call to a member function load() on null
[C:/UwAmp/www/test/app/models/User.php:12]
Where is wrong? Is this way good or not?
-models
----Model.php
----User.php
-controllers
----Controller.php
----UserController.php
UserController.php
class UserController extends Controller {
private $_usermodal;
public function __construct(){
parent::__construct();
}
function Register(){
$this->_usermodal = new User();
$this->_usermodal->getAll();
}
}
Model.php
class Model {
private $db;
public function __construct($table){
$this->db=new DB\SQL(
'mysql:host=localhost;port=3306;dbname=database',
'root',
'root'
);
return new DB\SQL\Mapper($this->db,$table);
}
}
User.php (model)
class User extends Model {
public function __construct(){
parent::__construct('users');
}
public function getAll(){
$x = $this->db->load('id = 1');
print_r($x);
}
}
Instead of returning a mapper instance from the __constructor (which is not possible in PHP), you could extend the framework mapper:
Model.php:
class Model extends DB\SQL\Mapper {
protected $db;
public function __construct($table) {
$this->db=new DB\SQL(
'mysql:host=localhost;port=3306;dbname=database',
'root',
'root'
);
parent::__construct($this->db,$table);
}
}
User.php:
class User extends Model {
public function __construct() {
parent::__construct('users');
}
public function getAll() {
return $this->find('',['order'=>'id']);
}
}
Now you can access your db records using the usual mapper methods:
$user = new User;
$user->load(['id=?',1]);
// or
foreach ($user->find(['name LIKE ?','%John%']) as $u)
echo $u->name;
or using your custom methods:
foreach ($user->getAll() as $u)
echo $u->name;
This structure can still be improved though as it creates a new DB\SQL instance for every fetched record. You could create a central DB service and use it by default in Model:
Index.php:
$f3->DB = function() {
if (!Registry::exists('DB')) {
$db=new DB\SQL(
'mysql:host=localhost;port=3306;dbname=database',
'root','root');
Registry::set('DB',$db);
}
return Registry::get('DB');
};
Model.php:
class Model extends DB\SQL\Mapper {
public function __construct($table,$db=NULL) {
if (!isset($db)) {
$f3=\Base::instance();
$db=$f3->DB();
}
parent::__construct($db,$table);
}
}

Interface not binding to implementation

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.

Categories