Kreait\Firebase\Exception\Database\DatabaseError 404 Not Found - laravel

I am new to Laravel so i have been trying to connect Laravel to a Firebase Realtime database but i am getting this error Kreait\Firebase\Exception\Database\DatabaseError
404 Not Found . I have the Service account in the controllers directory and properly referenced in the Firebase controller.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Kreait\Firebase;
use Kreait\Firebase\Factory;
use Kreait\Firebase\ServiceAccount;
use Kreait\Firebase\Database;
class FirebaseController extends Controller
{
$factory = (new Factory)->withServiceAccount(__DIR__.'/myServiceAccount.json');
$database = $factory->createDatabase();
$newPost = $database->getReference('myrealtime-default-rtdb/blog/posts')->push(['title' => 'Post title','body' => 'This should probably be longer.']);
echo"<pre>";
print_r($newPost->getvalue());
}
}
}

Using the new Factory pattern won't work (it's already handled by the Laravel Service Provider).
Personally I'd recommend using the app() helper. It's quite easy but before that make sure you've followed the installation process in the documentation. You might've missed something
Here's a link to that: https://github.com/kreait/laravel-firebase
Also make sure you include both the path to your Service Account json file and the database URL for your project in your .env file (I prefer using this method personally)
So in your .env file you should have something like
FIREBASE_CREDENTIALS = path_to_your_service_account_file.json
FIREBASE_DATABASE_URL= https://your-project-url.firebaseio.com/
(you can find this url when you open RealTime databases in your firebase console)
If you don't use auto-discovery make sure to add Kreait\Laravel\Firebase\ServiceProvider::class to your providers in the config/app.php file
run a vendor publish
Then in your controller you could have something like this
namespace App\Http\Controllers;
class FirebaseController extends Controller {
//
protected $database;
public function __construct()
{
$this->database = app('firebase.database');
}
public function index(){
$new_post = $this->database->getReference('your-reference')>push(["title" =>"something"]);
} }

Remember: on Linux, place Firebase config files etc. in a folder that user 'apache' can read! So, for example, do not place such files in /home/myname/firebase.json. Even if you do chmod 777 firebase.json, this file may not be accessible by user 'apache', hence the 404.
Then you do not need to use env variables.
$factory = (new Factory())->withServiceAccount(DIR.'/vendor/firebase-adminsdk.json');

Related

Laravel installation in sub-folder and horizon not working

I have installed the Laravel in sub-folder and is trying to install the horizon. After routing to "test.com/sub-folder/horizon", all the design in broken and also the internal links are pointing to main domain instead of main-domain-without-subfolder.
After the search, it seems to be the known issue which is already reported in github issue
Has there is any work around to make horizon work when Laravel is installed in sub-folder?
I have a solution that only involves PHP.
The issue, as pointed out by #Isaiahiroko, is the basePath defined for Horizon's interface. That code is in Laravel\Horizon\Http\Controllers\HomeController::index(). The idea is this: we are going to pass to Laravel's service container our own implementation of that controller that will override the basePath definition passed to Horizon's interface.
Create a new controller with code like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\Http\Controllers\HomeController;
class HorizonHomeController extends HomeController
{
/**
* Overrides default horizon route to support subdirectory hosting.
*/
public function index ()
{
// We use a plain request to check for the base url.
$request = request();
// Set up our base path.
$base_path = Str::substr($request->getBasePath(), 1);
if (!empty($base_path)) {
$base_path .= '/';
}
// Patch default horizon variables with our own base path.
$variables = Horizon::scriptVariables();
$variables['path'] = $base_path . config('horizon.path');
// Render horizon's home view.
return view('horizon::layout', [
'assetsAreCurrent' => Horizon::assetsAreCurrent(),
'horizonScriptVariables' => $variables,
'cssFile' => Horizon::$useDarkTheme ? 'app-dark.css' : 'app.css',
'isDownForMaintenance' => App::isDownForMaintenance(),
]);
}
}
What's left is telling Laravel's service container that when Horizon's HomeController is requested, it should provide our HorizonHomeController class. In your AppServiceProvider, at the end of the register() method, set this up:
// [...]
class AppServiceProvider extends ServiceProvider
{
// [...]
/**
* Register any application services.
*
* #return void
* #throws InvalidConfiguration
*/
public function register()
{
// [...]
// Horizon's subdirectory hack
$this->app->bind(
Laravel\Horizon\Http\Controllers\HomeController::class,
App\Http\Controllers\HorizonHomeController::class
);
}
// [...]
}
After that, you should be able to browse to http(s)://<your-host>/<your-sub-dir>/horizon normally.
Considerations:
To me this feels cleaner that patching a compiled js, which also has the downside that needs to be re-applied every time Horizon is updated (this can be mitigated with a post-update script in composer, tho). Also, for additional points, this solution is only overriding the method that renders the view, but not the route, which means all of Horizon's authentication mechanisms (middlewares and gates) are working exactly as described in the documentation.
If you desperately need to do this, here is a hack:
In public\vendor\horizon\app.js, search for window.Horizon.basePath
replace window.Horizon.basePath="/"+window.Horizon.path; with window.Horizon.basePath="/[you sub-directoy]/"+window.Horizon.path;
It should work...until you run update one day and it mysteriously stop working.

How to test a route in Laravel that uses both `Storage::put()` and `Storage::temporaryUrl()`?

I have a route in Laravel 7 that saves a file to a S3 disk and returns a temporary URL to it. Simplified the code looks like this:
Storage::disk('s3')->put('image.jpg', $file);
return Storage::disk('s3')->temporaryUrl('image.jpg');
I want to write a test for that route. This is normally straightforward with Laravel. I mock the storage with Storage::fake('s3') and assert the file creation with Storage::disk('s3')->assertExists('image.jpg').
The fake storage does not support Storage::temporaryUrl(). If trying to use that method it throws the following error:
This driver does not support creating temporary URLs.
A common work-a-round is to use Laravel's low level mocking API like this:
Storage::shouldReceive('temporaryUrl')
->once()
->andReturn('http://examples.com/a-temporary-url');
This solution is recommended in a LaraCasts thread and a GitHub issue about that limitation of Storage::fake().
Is there any way I can combine that two approaches to test a route that does both?
I would like to avoid reimplementing Storage::fake(). Also, I would like to avoid adding a check into the production code to not call Storage::temporaryUrl() if the environment is testing. The latter one is another work-a-round proposed in the LaraCasts thread already mentioned above.
I had the same problem and came up with the following solution:
$fakeFilesystem = Storage::fake('somediskname');
$proxyMockedFakeFilesystem = Mockery::mock($fakeFilesystem);
$proxyMockedFakeFilesystem->shouldReceive('temporaryUrl')
->andReturn('http://some-signed-url.test');
Storage::set('somediskname', $proxyMockedFakeFilesystem);
Now Storage::disk('somediskname')->temporaryUrl('somefile.png', now()->addMinutes(20)) returns http://some-signed-url.test and I can actually store files in the temporary filesystem that Storage::fake() provides without any further changes.
Re #abenevaut answer above, and the problems experienced in the comments - the call to Storage::disk() also needs mocking - something like:
Storage::fake('s3');
Storage::shouldReceive('disk')
->andReturn(
new class()
{
public function temporaryUrl($path)
{
return 'https://mock-aws.com/' . $path;
}
}
);
$expectedUrl = Storage::disk('s3')->temporaryUrl(
'some-path',
now()->addMinutes(5)
);
$this->assertEquals('https://mock-aws.com/some-path', $expectedUrl);
You can follow this article https://laravel-news.com/testing-file-uploads-with-laravel, and mix it with your needs like follow; Mocks seem cumulative:
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
public function testAvatarUpload()
{
$temporaryUrl = 'http://examples.com/a-temporary-url';
Storage::fake('avatars');
/*
* >>> Your Specific Asserts HERE
*/
Storage::shouldReceive('temporaryUrl')
->once()
->andReturn($temporaryUrl);
$response = $this->json('POST', '/avatar', [
'avatar' => UploadedFile::fake()->image('avatar.jpg')
]);
$this->assertContains($response, $temporaryUrl);
// Assert the file was stored...
Storage::disk('avatars')->assertExists('avatar.jpg');
// Assert a file does not exist...
Storage::disk('avatars')->assertMissing('missing.jpg');
}
}
Another exemple for console feature tests:
command : https://github.com/abenevaut/pokemon-friends.com/blob/1.1.3/app/Console/Commands/PushFileToAwsCommand.php
test : https://github.com/abenevaut/pokemon-friends.com/blob/1.1.6/tests/Feature/Console/Files/PushFileToCloudCommandTest.php

How to use global variables in Laravel

Is it possible to share and change some variable between multiple views? For example, I want to have a variable $user that will be shared between all views. When a user logs the variable is set up, when the user logs out, the variable is unset. I was unable to achieve requested using
the following combination:
in AppServiceProvider:
view()->share('var', 1);
in the controller:
$var = view()->shared('var');.
$var ++;
view()->share('var', var);
return view(''', 'var'=>$var)
Every time when the page is reloaded $var is always the same (2).
I want to have a variable $user that will be shared between all views
You should use auth()->user() to get authenticated user instance in any view.
But if you don't want to use it for some reason, you could share the variable between multiple views with a view composer.
share() method will be useful only if you want to share a variable with all views. To make it work, put view()->share('key', 'value') to the boot() method of a service provider.
Also, the code in your controller looks like you want to share data not between views, but between requests. Use session for that.
To save the data:
session(['key' => 'value']);
To get the data in another request:
session('key');
It would be better to add another service provider. Take a look at my provider:
<?php
namespace App\Providers;
use Request;
use Illuminate\Support\ServiceProvider;
class ViewComposerServiceProvider extends ServiceProvider
{
public function boot()
{
$this->globalThings();
//call another globals' function here
}
public function register()
{
//
}
/**
* Get the golbals
*/
private function globalThings()
{
view()->composer(array('*.*'),function($view){
//get the data however you want it!
$view->with('global', Model::where('field','value')->get());
});
}
And don't forget to add the service provider to list of provider is config/app.php
App\Providers\ViewComposerServiceProvider::class,

I can't find the implementation for Storage Facade in laravel

I'm new with laravel and I'm working in fileststem on laravel
(I want to do usual fileststem process like -make dir - copy - put -delete -ect)
I'm using laravel "Storage" Facade
but when i type
i referenced the class above like this in my code
use Illuminate\Support\Facades\Storage;
for example below :
if (file_exists(public_path($oldImage))) {
Storage::delete($oldImage);
}
nothing happens ,and when i refer to the class code i found this :
namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Filesystem\FilesystemManager
*/
class Storage extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'filesystem';
}
}
so where is the implementation and if you have alternative way to deal with
filesystem process rather than "Storage" facade ??
Storage is a facade and accesses the class Filesystem located here: vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php
As you can see in the official filesystem documentation the code snippets use Storage.
UPDATE:
You should add use Storage; to be able to use the Storage facade.
I recommend reading the Laravel 8.X docs to get an initial heads up: https://laravel.com/docs/8.x/filesystem
NOTE: Before you get too carried away, make sure you understand the difference between local and public.
For starters, you should make your first goal to upload a file and acquire the UploadedFile type.
You can access a single file via something like $request->file('name'), or an array of images via something like:
// $request->input('images')
foreach ($images as $image) {
\Log::debug($image->getClientOriginalName());
}
If your file upload can be single and/or multiple, I recommend going with the array approach because a single file wrapped in an array allows you to use the same syntax for single and multi uploads (ie: that foreach loop works fine with one image, no extra code).
Here's an example:
use Illuminate\Support\Facades\Storage;
$slug = 'davids-sandwich-photos';
foreach ($images as $image) {
Storage::putFileAs(
'images' .'/'. $slug,
$image,
$image->getClientOriginalName()
);
}
Storage::putFileAs() can take 3 parameters: directory, content, filename. You can see above in the code that I interpolated a mix of static and derived directory name. You could do something like 'images' .'/'. $slug .'/'. Auth::user()->id to save the file in /images/davids-sandwich-photos/11.
Then, check in your repo directory: /storage/app/ and look for the images directory.
You can manually delete the folders while testing to get your bearings straight.
That should be enough to get most people started.
To avoid using the Storage facade, you can use:
foreach ($images as $image) {
$image->storeAs(
'examples' .'/'. $slug,
$image->getClientOriginalName(),
'public'
);
}
--
Check out config/filesystems.php under the disks section if you want to start manipulating the drivers, but I'm not a DB admin expert here.
I also saved this along my journey: https://medium.com/#shafiya.ariff23/how-to-store-uploaded-images-in-public-folder-in-laravel-5-8-and-display-them-on-shared-hosting-e31c7f37a737. You might need that if you get stuck with something like symlinking.
<img
v-for="image in example.images"
:key="image.filename"
:src="`/storage/examples/${example.slug}/${image.filename}`"
>
NOTE: The important part with Vue JS is to use <img src="/storage/examples/slug/filename.jpg"> if your file is located in your repository as /storage/app/public/examples/slug/filename.jpg Pay close attention to every character.
The public_path function returns the fully qualified path to the public directory ie public directory inside the laravel application. When using Storage, the path is set to the storage/app directory.
if (file_exists(public_path($oldImage))) {
//public_path($oldImage) will check for file in public directory
Storage::delete($oldImage); //Will delete file in storage/app directory
}
The modified code should be
if(Storage::has($oldImage)){
Storage::delete($oldImage);
}

How to protect image from public view in Laravel 5?

I have installed Laravel 5.0 and have made Authentication. Everything is working just fine.
My web site is only open for Authenticated members. The content inside is protected to Authenticated members only, but the images inside the site is not protected for public view.
Any one writes the image URL directly can see the image, even if the person is not logged in to the system.
http://www.somedomainname.net/images/users/userImage.jpg
My Question: is it possible to protect images (the above URL example) from public view, in other Word if a URL of the image send to any person, the individual must be member and login to be able to see the image.
Is that possible and how?
It is possible to protect images from public view in Laravel 5.x folder.
Create images folder under storage folder (I have chosen storage folder because it has write permission already that I can use when I upload images to it) in Laravel like storage/app/images.
Move the images you want to protect from public folder to the new created images folder. You could also chose other location to create images folder but not inside the public folder, but with in Laravel folder structure but still a logical location example not inside controller folder. Next you need to create a route and image controller.
Create Route
Route::get('images/users/{user_id}/{slug}', [
'as' => 'images.show',
'uses' => 'ImagesController#show',
'middleware' => 'auth',
]);
The route will forward all image request access to Authentication page if person is not logged in.
Create ImagesController
class ImagesController extends Controller {
public function show($user_id, $slug)
{
$storagePath = storage_path('app/images/users/' . $user_id . '/' . $slug);
return Image::make($storagePath)->response();
}
}
EDIT (NOTE)
For those who use Laravel 5.2 and newer. Laravel introduces new and better way to serve files that has less overhead (This way does not regenerate the file as mentioned in the answer):
File Responses
The file method can be used to display a file, such as an image or
PDF, directly in the user's browser instead of initiating a download.
This method accepts the path to the file as its first argument and an
array of headers as its second argument:
return response()->file($pathToFile);
return response()->file($pathToFile, $headers);
You can modify your storage path and file/folder structure as you wish to fit your requirement, this is just to demonstrate how I did it and how it works.
You can also added condition to show the images only for specific members in the controller.
It is also possible to hash the file name with file name, time stamp and other variables in addition.
Addition: some asked if this method can be used as alternative to public folder upload, YES it is possible but it is not recommended practice as explained in this answer. So the same method can be also used to upload images in storage path even if you do not intend to protect them, just follow the same process but remove 'middleware' => 'auth',. That way you won't give 777 permission in your public folder and still have a safe uploading environment. The same mentioned answer also explain how to use this method with out authentication in case some one would use it or giving alternative solution as well.
In a previous project I protected the uploads by doing the following:
Created Storage Disk:
config/filesystems.php
'myDisk' => [
'driver' => 'local',
'root' => storage_path('app/uploads'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'private',
],
This will upload the files to \storage\app\uploads\ which is not available to public viewing.
To save files on your controller:
Storage::disk('myDisk')->put('/ANY FOLDER NAME/' . $file, $data);
In order for users to view the files and to protect the uploads from unauthorized access. First check if the file exist on the disk:
public function returnFile($file)
{
//This method will look for the file and get it from drive
$path = storage_path('app/uploads/ANY FOLDER NAME/' . $file);
try {
$file = File::get($path);
$type = File::mimeType($path);
$response = Response::make($file, 200);
$response->header("Content-Type", $type);
return $response;
} catch (FileNotFoundException $exception) {
abort(404);
}
}
Serve the file if the user have the right access:
public function licenceFileShow($file)
{
/**
*Make sure the #param $file has a dot
* Then check if the user has Admin Role. If true serve else
*/
if (strpos($file, '.') !== false) {
if (Auth::user()->hasAnyRole(['Admin'])) {
/** Serve the file for the Admin*/
return $this->returnFile($file);
} else {
/**Logic to check if the request is from file owner**/
return $this->returnFile($file);
}
} else {
//Invalid file name given
return redirect()->route('home');
}
}
Finally on Web.php routes:
Route::get('uploads/user-files/{filename}', 'MiscController#licenceFileShow');
I haven't actually tried this but I found Nginx auth_request module that allows you to check the authentication from Laravel, but still send the file using Nginx.
It sends an internal request to given url and checks the http code for success (2xx) or failure (4xx) and on success, lets the user download the file.
Edit: Another option is something I've tried and it seemed to work fine. You can use X-Accel-Redirect -header to serve the file from Nginx. The request goes through PHP, but instead of sending the whole file through, it just sends the file location to Nginx which then serves it to the client.
if I am understanding you it's like !
Route::post('/download/{id}', function(Request $request , $id){
{
if(\Auth::user()->id == $id) {
return \Storage::download($request->f);
}
else {
\Session::flash('error' , 'Access deny');
return back();
}
}
})->name('download')->middleware('auth:owner,admin,web');
Every file inside the public folder is accessible in the browser. Anyone easily gets that file if they find out the file name and storage path.
So better option is to store the file outside the public folder eg: /storage/app/private
Now do following steps:
create a route (eg: private/{file_name})
Route::get('/private/{file_name}', [App\Http\Controllers\FileController::class, 'view'])->middleware(['auth'])->name('view.file');
create a function in a controller that returns a file path. to create a controller run the command php artisan make:controller FileController
and paste the view function in FileController
public function view($file)
{
$filePath = "notes/{$file}";
if(Storage::exists($filePath)){
return Storage::response($filePath);
}
abort(404);
}
then, paste use Illuminate\Support\Facades\Storage; in FileController for Storage
And don't forget to assign middleware (in route or controller) as your requirement(eg: auth)
And now, only those who have access to that middleware can access that file through a route name called view.file

Resources