Lumen Cache\Store is not instantiable - caching

I'm quite new to Laravel and Lumen, so my question may be a little simple, but I couldn't find any useful answer yet.
Lumen version is 5.1.
So I tried to create a data repository supported by a cache. Firstly I want to use FileStore, then I want to move to some more appropriate.
I tried to inject Cache repository like this:
<?php
namespace App\Repositories;
use Illuminate\Cache\Repository;
class DataRepository
{
private $cache;
public function __construct(Repository $cache)
{
$this->cache = $cache;
}
}
It seemed pretty simple to me. But when I try to use this repo in my controller, and tried to inject this repo into it, during instantiation I get the following error:
BindingResolutionException in Container.php line 749:
Target [Illuminate\Contracts\Cache\Store] is not instantiable.
I guessed the repository cannot find the matching and useable store implementation. When I tried to bind the Store to \Illumante\Cache\FileStore like this:
$this->app->bind(\Illuminate\Contracts\Cache\Store::class, \Illuminate\Cache\FileStore::class);
I got a new kind of error:
Unresolvable dependency resolving [Parameter #1 [ <required> $directory ]] in class Illuminate\Cache\FileStore
I guess I have a more complicated config issue, so I didn't want to walk through the dependency tree.
In my .env I have these:
CACHE_DRIVER=file and SESSION_DRIVER=file
In Lumen I explicitly enabled the facades, the DotEnv (and the eloquent also for my data repositories).
Dotenv::load(__DIR__.'/../');
$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);
$app->withFacades();
$app->withEloquent();
I tried to add a cache.php configuration. In the bootstrap/app.php I added $app->configure('cache'); to use it with the following configs:
<?php
return [
'default' => env('CACHE_DRIVER', 'file'),
'stores' => [
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache'),
],
],
];
Could you help me, how can I bootstrap the Cache properly?

Answer
The cache implementation in Lumen is registered as:
Illuminate\Contracts\Cache\Repository
Not
Illuminate\Cache\Repository
So you may change your code to:
<?php
namespace App\Repositories;
use Illuminate\Contracts\Cache\Repository;
class DataRepository
{
private $cache;
public function __construct(Repository $cache)
{
$this->cache = $cache;
}
}
P.S You don't need to configure cache, since Lumen will configure any cache configuration automatically.
Tricks
But if you still want to use Illuminate\Cache\Repository, you may bind it first in your ServiceProvider or bootstrap/app.php file:
use Illuminate\Cache\Repository as CacheImplementation;
use Illuminate\Contracts\Cache\Repository as CacheContract;
$app->singleton(CacheImplementation::class, CacheContract::class);

Related

Upload to Dropbox not working from Laravel with Dropbox driver

I'm trying to upload a (DomPDF generated) PDF file to Dropbox with the Dropbox driver in Laravel 8. I've installed spatie/flysystem-dropbox and created a DropboxServiceProvider.php with following contents:
<?php
namespace App\Providers;
use Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client;
use Spatie\FlysystemDropbox\DropboxAdapter;
class DropboxServiceProvider extends ServiceProvider
{
public function boot()
{
Storage::extend('dropbox', function ($app, $config) {
$client = new Client([$config['key'], $config['secret']]);
return new Filesystem(new DropboxAdapter($client));
});
}
public function register()
{
//
}
}
The service provider is also added to my config/app providers:
'providers' => [
...
App\Providers\DropboxServiceProvider::class,
...
]
In my config/filesystems I've added the dropbox driver (dropbox app key and secret are also set in .env file):
'dropbox' => [
'driver' => 'dropbox',
'key' => env('DROPBOX_APP_KEY'),
'secret' => env('DROPBOX_APP_SECRET'),
]
Now, when I try to run the following code, it returns false and the file doesn't appear in my Dropbox. When I change the disk to 'local', the file gets uploaded to my local storage.
$path = "pdf/file.pdf";
$storage_path = Storage::path($path);
$contents = file_get_contents($storage_path);
$upload = Storage::disk('dropbox')->put($path, $contents);
return $upload;
I've already tried clearing my config by running php artisan config:clear. After trying many different things, I have no idea what I'm doing wrong, so any advice will be appreciated!
The problem was not in the code, but in the permissions in my dropbox app: files.content.write wasn't enabled yet.

Laravel - get size from uploaded file

I have saved a file with this command
$newFile = [
'event_id' => $event->id,
'path' => $storePath
];
EventFile::create($newFile);
I can get the path to the file for a link like this:
Storage::disk('public')->url($file->path);
But there is no data about the file size. How can i get the file size in blade view???
Laravel 5^
$request->file('file')->getSize(); // in bytes
Laravel 4
$request->file('file')->getClientSize(); // getClientSize() is deprecated in Laravel 5
The more Simpler way is to use Storage Facade if you have already stored / uploaded file
use Illuminate\Support\Facades\Storage;
public function get_size($file_path)
{
return Storage::size($file_path);
}
Or if you are using S3 Bucket then you can modify the above function like below
use Illuminate\Support\Facades\Storage;
public function get_size($file_path)
{
return Storage::disk('s3')->size($file_path);
}
Check Laravel File Storage
getClientSize() is deprecated starting version 4.1. Use getSize() instead.
https://github.com/symfony/symfony/blob/4.1/UPGRADE-4.1.md#httpfoundation
Very simple(Proper Laravel Way):
//add this at the top of your controller
use File;
// add this with in your function
$size = File::size($PATH_OF_FILE);
laravel 8
$imageSize = $request->file('file')->getSize();
$fil->size = number_format($imageSize / 1048576,2);
$file->size My Table Change It With Your DB Table
According to the Laravel Documentation for version 8.0:
When using the local driver, all files that should be publicly accessible should be placed in the storage/app/public directory.
So the root of your local storage here is the public/ directory within storage/app/
You can Specify the method to get file size in your File Model like this
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class File extends Model
{
use HasFactory;
protected $fillable = [
'name',
'file_path'
];
public function getFileSize()
{
// Ensure $this->file_path begins with '/public/';
return Storage::size($this->file_path);
}
}
And use it like this within your blade file
<div>{{ $file->getFileSize() }}</div>
If you use it this way, $this->file_path must have the format: /public/optional_subdirectory/file_name

How can I use Illuminate\Session\Middleware\StartSession::class on lumen 5.2

I got this Error:
BindingResolutionException in Container.php line 839:
Unresolvable dependency resolving
[Parameter #0 [ <required> $app ]] in class Illuminate\Support\Manager
bootstrap/app.php :
$app->middleware([
Illuminate\Session\Middleware\StartSession::class,
]);
Here's a recap of what needs to be done to activate sessions in Lumen (tested on Lument 5.4):
config/session.php
Download session config from Laravel repo.
bootstrap/app.php
// Load session config (otherwise it won't be loaded)
$app->configure('session');
// Add `Session` middleware
$app->middleware(Illuminate\Session\Middleware\StartSession::class);
// Add `SessionServiceProvider`
$app->register(Illuminate\Session\SessionServiceProvider::class);
// fix `BindingResolutionException` problem
$app->bind(Illuminate\Session\SessionManager::class, function ($app) {
return $app->make('session');
});
After that you can access session with app('session') in your controller.
Before adding StartSession middleware, inject this dependency to container:
$app->bind(Illuminate\Session\SessionManager::class, function ($app) {
return new Illuminate\Session\SessionManager($app);
});
$app->middleware([
Illuminate\Session\Middleware\StartSession::class,
]);

Cache files permissions fix for Kohana Twig module

How to configure Kohana + Twig module so the Twig will set "writable by all" permissions on all of it's cache directory and it's descendant files?
So, for example, when I run my application through the Apache module (mod_php) and cache file owner is apache (or httpd) user, I will be able to remove cache files (to clean the cache or completely remove whole application) using regular user and ssh access.
I'm able to do it with Kohana's cache, but Twig's cache is created somehow differently.
It's not very easy, but not too complicated either. I have achieved state presented below by trial-and-error method.
Create a class that inherits from Twig_Cache_Filesystem and will be used instead of it. Check this out:
<?php
namespace Application\Twig;
class Cache_Filesystem extends \Twig_Cache_Filesystem
{
public function write($key, $content)
{
$old = umask(0000);
parent::write($key, $content);
umask($old);
}
}
Note, that this class must have it's name unique, so it is a good idea to namespace it. Also, it must be accessible to other code, so consider using composer's autoloading feature.
This is the fix itself, rest of the guide is just the way of implementing it into Kohana+Twig ecosystem.
Copy Twig.php from modules/kohana-twig/classes/Twig.php into your application's directory, i.e. application/classes/Twig.php (thank you Kohana's Cascading Filesystem!)
Modify a bit newly copied file, to let Twig_CacheInterface instance be passed in the config file (application/config/twig.php) instead of just a simple string (specifying to the Twig's cache directory). Take a look of my example:
<?php defined('SYSPATH') or die('No direct script access.');
class Twig extends Kohana_Twig
{
/**
* Initialize the Twig module
*
* #throws Kohana_Exception
* #return bool
*/
public static function init()
{
$path = Kohana::$config->load('twig.environment.cache');
if (is_string($path)) {
return parent::init();
} else if ($path instanceof Twig_CacheInterface) {
return true;
}
throw new Kohana_Exception('Twig cache could not be initialized');
}
}
In configuration file for kohana-twig module, i.e. application/config/twig.php (if not yet copied from module to your application, do it now), define environment.cache key like this:
return array(
'loader' => array(
'extension' => 'twig',
'path' => 'views',
),
'environment' => array(
'auto_reload' => (Kohana::$environment >= Kohana::TESTING),
'autoescape' => true,
'base_template_class' => 'Twig_Template',
// Following line is related to this issue and fix:
'cache' => new \Application\Twig\Cache_Filesystem(APPPATH . 'cache/twig'),
'charset' => 'utf-8',
'optimizations' => - 1,
'strict_variables' => false,
),
'functions' => array(),
'filters' => array(),
'tests' => array(),
}
This works for me. Hopefully it will help someone struggling with similar problem.

How to correctly instantiate the Iluminate class outside of laravel

I have Eloquent working outside of Laravel with no problems. Now Im trying to use also the Validation class by:
<?php
namespace User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Validator;
class User extends Model {
private $rules = array(
'firstName' => 'required|min:2|max:50',
'lastName' => 'required|min:2|max:50',
);
public function validate($data)
{
$v = Validator::make($data, $this->rules);
return $v->passes();
}
}
Executing that code give me an error:
Call to undefined method Illuminate\Validation\Validator::make()
That is correct since the method make is not on the class Validator but in his factory:
How can I correctly instantiate the Validation lib in order to get this working?
BTW, this is my composer.json:
{
"require": {
"slim/slim": "^2.6",
"illuminate/database": "^5.1",
"illuminate/validation": "^5.1"
},
"autoload": {
"classmap": [
"app/model"
]
}
}
To get the validation package to work outside of Laravel, you'll also need the translation package.
So first create an instance of the translator, and then use that to make a validation factory.
Working example
<?php
/*
Required composer packages:
illuminate/validation
illuminate/translation
*/
/*
Translation language files directory is the same as with Laravel
./lang/en/validation.php
*/
require_once 'vendor/autoload.php';
// You need to specify where the translation files is
$test_translation_path = __DIR__.'/lang';
$test_translation_locale = 'en';
// Set up data for the validator
$test_input_data = ['field' => 'value'];
$test_input_rules = ['field' => 'required'];
$translation_file_loader = new Illuminate\Translation\FileLoader(new Illuminate\Filesystem\Filesystem, $test_translation_path);
$translator = new Illuminate\Translation\Translator($translation_file_loader, $test_translation_locale);
$validation_factory = new Illuminate\Validation\Factory($translator);
$validator = $validation_factory->make($test_input_data, $test_input_rules);
if ($validator->fails()) {
die('Validation failed');
}
die('Validation passed!');
There are a few issues with your approach:
you're trying to create a new Validator instance by calling the Illuminate\Validation\Validator::make method, yet you point out that the make() method is present on the Illuminate\Validation\Factory which is a different class altogether, so the error you're getting is justified.
you're trying to call the make() method statically :: when in fact it's not defined as such.
you're trying to use the Validator the same as you would in a Laravel application enviroment, which won't work because you're missing the Laravel Facades and Service Providers infrastructure that Laravel uses to allow for such a simple instantiation of the Validator.
If you were to look at the registerValidationFactory() method inside the Illuminate\Validation\ValidationServiceProvider class, you'd get a sense of how the validator instance is created. So based on that, you could do the following:
namespace User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Factory as ValidatorFactory;
use Symfony\Component\Translation\Translator;
class User extends Model {
private $rules = array(
'firstName' => 'required|min:2|max:50',
'lastName' => 'required|min:2|max:50',
);
public function validate($data)
{
$factory = new ValidatorFactory(new Translator('en'));
$v = $factory->make($data, $rules);
return $v->passes();
}
}

Resources