retrieve images from different directory - laravel

I have two laravel projects on the same server connected to the same database on the Cpanel, the first project is the main project and the other one is a subdomain project.
I want to view images for the subdomain project from the images folder in the main project.
I tried to return back to the directory but it didn't work
<img src="{{ url('../images/'.$item->image)}}">
however, when I store images from the subdomain project to the images folder in the main project, it works well. but I can't retrieve them back to the view.

Ok - so here's what you could do to make it work.
If you don't have already, create your own helpers file and add it to composer's autoload:
app/helpers.php
<?php
if (! function_exists('parentAsset')) {
/**
* Generate a parentAsset path for the application.
*
* #param string $path
* #param bool|null $secure
* #return string
*/
function parentAsset($path, $secure = null)
{
return app('parentUrl')->asset($path, $secure);
}
}
composer.json
{
//...
"autoload": {
"psr-4": {
"App\\": "app/"
},
"classmap": [
"database/seeds",
"database/factories"
],
"files": [
"app/helpers.php"
]
},
//...
}
Register parentUrl in the container
app/Providers/AppServiceProvider.php
use Illuminate\Routing\UrlGenerator;
public function register()
{
$this->app->singleton('parentUrl', function ($app) {
$routes = $app['router']->getRoutes();
$app->instance('routes', $routes);
return new UrlGenerator(
$routes,
$app->rebinding('request', $this->requestRebinder()),
$app['config']['app.parent_asset_url']
);
});
}
/**
* Get the URL generator request rebinder.
*
* #return \Closure
*/
protected function requestRebinder()
{
return function ($app, $request) {
$app['url']->setRequest($request);
};
}
Please note the new config entry for app namespace: parent_asset_url. You now need to add it to your app.php config file.
config/app.php
[
//...
'asset_url' => env('ASSET_URL', null),
'parent_asset_url' => env('PARENT_ASSET_URL', null),
//...
]
Lastly you need to add PARENT_ASSET_URL variable to your .env file and specify the url of your parent application.
PARENT_ASSET_URL=https://google.com
Recompile autoloader
composer dump-autoload -o
And you can now use the parentAsset helper to load files directly from the parent domain:
<img src="{{ parentAsset('/assets/images/logo.svg') }}">
Hope this helps.

Related

Laravel Jetstream livewire - Profile photo is not being uploaded on s3

I have checked everything in AWS S3, permissions, policy bucket name, all looks okay.
I have updated the config in the jetstream config file
config\jetstream.php
'profile_photo_disk' => 's3',
I have updated the .env file variables with AWS S3 values, but the profile photo has not been uploaded on S3.
The same credentials are working on simple Laravel upload code, the image is successfully uploaded on s3, but by using Jetstream, it is not working, also there is no error reflected on the screen and in the log file as well.
Anyone could please help
There is a function written in the library trait, path and function is as below
Path: \vendor\laravel\jetstrem\src\HasProfilePhoto.php
Function:
/**
* Update the user's profile photo.
*
* #param \Illuminate\Http\UploadedFile $photo
* #return void
*/
public function updateProfilePhoto(UploadedFile $photo)
{
tap($this->profile_photo_path, function ($previous) use ($photo) {
$this->forceFill([
'profile_photo_path' => $photo->storePublicly(
'profile-photos', ['disk' => $this->profilePhotoDisk()]
),
])->save();
if ($previous) {
Storage::disk($this->profilePhotoDisk())->delete($previous);
}
});
}
As you can see for uploading the profile image on s3 the function used is storePublicly, but in my case, the s3 policy is not defined as publicly read, so I have overloaded the same function in
app\Actions\Fortify\UpdateUserProfileInformation.php
On top of the class add the Trait as use HasProfilePhoto;
/**
* Update the user's profile photo.
*
* #param \Illuminate\Http\UploadedFile $photo
* #return void
*
*
*/
public function updateProfilePhoto($user, UploadedFile $photo)
{
tap($user->profile_photo_path, function ($previous) use ($photo, $user) {
$user->forceFill([
'profile_photo_path' => $photo->store(
'profile-photos', ['disk' => config('jetstream.profile_photo_disk')]
),
])->save();
if ($previous) {
\Storage::disk(config('jetstream.profile_photo_disk'))->delete($previous);
}
});
}
This works for me, I hope if you face similar issue, then it would help you.
The problem is related to the CORS policy on the S3 bucket.
I overwrote to use ->store(...) instead of ->storePublicly(...), which solved my issue.
Original:
public function updateProfilePhoto(UploadedFile $photo)
{
tap($this->profile_photo_path, function ($previous) use ($photo) {
$this->forceFill([
'profile_photo_path' => $photo->storePublicly(
'profile-photos', ['disk' => $this->profilePhotoDisk()]
),
])->save();
if ($previous) {
Storage::disk($this->profilePhotoDisk())->delete($previous);
}
});
}
New:
public function updateProfilePhoto(UploadedFile $photo)
{
tap($this->profile_photo_path, function ($previous) use ($photo) {
$this->forceFill([
'profile_photo_path' => $photo->store(
'profile-photos', ['disk' => $this->profilePhotoDisk()]
),
])->save();
if ($previous) {
Storage::disk($this->profilePhotoDisk())->delete($previous);
}
});
}

Use Auth in AppServiceProvider

I need the ID of the user who is logged in to get a photo in the profile table, here I am trying to use View but only in the index function that gets $profile, I want all files in the view to have $profile
public function index(){
$profil = Profil_user::where('user_id',$auth)->first();
View::share('profil', $profil);
return view('user.index');
}
I have also tried AppServiceProvider but I get an error in the form of a null value if I don't log in, is there a solution to my problem?
public function boot(){
$auth = Auth::user();
dd($auth);
}
exist several way to pass a variable to all views. I explain some ways.
1. use middleware for all routes that you need to pass variable to those:
create middleware (I named it RootMiddleware)
php artisan make:middleware RootMiddleware
go to app/Http/Middleware/RootMiddleware.php and do following example code:
public function handle($request, Closure $next) {
if(auth()->check()) {
$authUser = auth()->user();
$profil = Profil_user::where('user_id',$authUser->id)->first();
view()->share([
'profil', $profil
]);
}
return $next($request);
}
then must register this middleware in app/Http/Kernel.php and put this line 'root' => RootMiddleware::class, to protected $routeMiddleware array.
then use this middleware of routes or routes group, for example:
Route::group(['middleware' => 'root'], function (){
// your routes that need to $profil, of course it can be used for all routers(because in handle function in RootMiddleware you set if
});
or set for single root:
Route::get('/profile', 'ProfileController#profile')->name('profile')->middleware('RootMiddleware');
2. other way that you pass variable to all views with view composer
go to app/Http and create Composers folder and inside it create ProfileComposer.php, inside ProfileComposer.php like this:
<?php
namespace App\Http\View\Composers;
use Illuminate\View\View;
class ProfileComposer
{
public function __construct()
{
}
public function compose(View $view)
{
$profil = Profil_user::where('user_id', auth()->id)->first();
$view->with([
'profil' => $profil
]);
}
}
now it's time create your service provider class, I named it ComposerServiceProvider
write this command in terminal : php artisan make:provider ComposerServiceProvider
after get Provider created successfully. message go to config/app.php and register your provider with put this \App\Providers\ComposerServiceProvider::class to providers array.
now go to app/Providers/ComposerServiceProvider.php and do like following:
namespace App\Providers;
use App\Http\View\Composers\ProfileComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
View::composer(
'*' , ProfileComposer::class // is better in your case use write your views that want to send $profil variable to those
);
/* for certain some view */
//View::composer(
// ['profile', 'dashboard'] , ProfileComposer::class
//);
/* for single view */
//View::composer(
// 'app.user.profile' , ProfileComposer::class
//);
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
3. is possible that without create a service provider share your variable in AppServiceProvider, go to app/Provider/AppServiceProvider.php and do as follows:
// Using class based composers...
View::composer(
'profile', 'App\Http\View\Composers\ProfileComposer'
);
// Using Closure based composers...
View::composer('dashboard', function ($view) {
//
});
I hope be useful
you can use this
view()->composer('*', function($view)
{
if (Auth::check()) {
$view->with('currentUser', Auth::user());
}else {
$view->with('currentUser', null);
}
});

Dingo API remove "data" envelope

is it there an easy way to remove the "data" envelope from the Dingo API response.
When I use this Transformer to transform user models:
class UserTransformer extends EloquentModelTransformer
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'roles'
];
protected $defaultIncludes = [
'roles'
];
public function transform($model)
{
if(! $model instanceof User)
throw new InvalidArgumentException($model);
return [
'id' => $model->id,
'name' => $model->name,
'email' => $model->email
];
}
/**
* Include Roles
*
* #param User $user
* #return \League\Fractal\Resource\Item
*/
public function includeRoles(User $user)
{
$roles = $user->roles;
return $this->collection($roles, new RoleTransformer());
}
I get this response:
{
data : [
"id": 102,
"name": "Simo",
"email": "mail#outlook.com",
"roles": {
"data": [
{
"id": 1
"name": "admin"
}
]
}
}
]
}
I read some articles about RESTful APIs and a lot of them stated that such enveloped responses arent very modern (You should use the HTTP Header instead).
How can I disable this behaviour at least for the includes?
Thank you
For those who fall on this later and as I had really hard time to make it, I'd like to share how I made it working in my API :
1) Create a Custom Serializer, NoDataArraySerializer.php :
namespace App\Api\V1\Serializers;
use League\Fractal\Serializer\ArraySerializer;
class NoDataArraySerializer extends ArraySerializer
{
/**
* Serialize a collection.
*/
public function collection($resourceKey, array $data)
{
return ($resourceKey) ? [ $resourceKey => $data ] : $data;
}
/**
* Serialize an item.
*/
public function item($resourceKey, array $data)
{
return ($resourceKey) ? [ $resourceKey => $data ] : $data;
}
}
2) Set new the Serializer. In bootstrap/app.php, add :
$app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
$fractal = new League\Fractal\Manager;
$fractal->setSerializer(new App\Api\V1\Serializers\NoDataArraySerializer);
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});
That's it.
Now, in your UserController (for instance), you can use it like this :
namespace App\Api\V1\Controllers;
use App\Api\V1\Models\User;
use App\Api\V1\Transformers\UserTransformer;
class UserController extends Controller
{
public function index()
{
$items = User::all();
return $this->response->collection($items, new UserTransformer());
}
}
And the response will look like :
[
{
"user_id": 1,
...
},
{
"user_id": 2,
...
}
]
Or, I you want to add an enveloppe, you just need to set the resource key in the Controller. Replace :
return $this->response->collection($items, new UserTransformer());
by
return $this->response->collection($items, new UserTransformer(), ['key' => 'users']);
And the response will look like :
{
"users": [
{
"user_id": 1,
...
},
{
"user_id": 2,
...
}
]
}
One addition to the solution of YouHieng. The preferred way to register the NoDataArraySerializer in Laravel 5.3 and above is to write a custom ServiceProvider and add the logic into the boot() method and not the bootstrap/app.php file.
For Example:
php artisan make:provider DingoSerializerProvider
Then:
public function boot(){
$this->app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
$fractal = new League\Fractal\Manager;
$fractal->setSerializer(new App\Http\Serializers\NoDataArraySerializer());
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});
}
Have a look to http://fractal.thephpleague.com/serializers/#arrayserializer. They explain exactly what to do when
Sometimes people want to remove that 'data' namespace for items

Add your own helpers.php to composer.json

Following this topic Use a custom function everywhere in the website I need a bit of help to finish what has been started.
So I created a folder in the app folder: Customs
Then I created a helpers.php file that has the following code:
<?php
use Illuminate\Support\Str;
if (! function_exists('str_slug')) {
/**
* Generate a URL friendly "slug" from a given string.
*
* #param string $title
* #param string $separator
* #return string
*/
function my_slug($title, $separator = '-')
{
$title = str_replace('\'','_',$title);
return Str::slug($title, $separator);
}
}
I read that I now have to update my composer.json, and especially the autoload section which is basically:
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
}
},
I don't understand what I should do now... psr-4 already says that the whole app folder is autoloaded, no?
I also tried to put the full path to the helpers.php but it did not work either.
What am I doing wrong?
Your autoload should have something like that:
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
},
"files": [
"app/helpers.php"
]
},
where files are your custom files. Also as is mentioned in Use a custom function everywhere in the website question I advice you to use traits for e.g. trait StringSluggify. It keeps OOP way.
Create a folder inside of app/ called Helpers.
Inside of app/Helpers create your custom class
namespace App\Helpers;
class FooHelper{
public static function bar(){
return 'bar';
}
}
Run composer dump-autoload to update autoload.
Here you are now able to use your helper functions like:
$bar = \App\Helper\FooHelper::bar();
If your plan is attach a facade, then edit facades array in config/app.php like so:
'Foo' => \App\Helpers\FooHelper::class
Now you can call your functions like:
public function controllerFunction(){
$bar = \Foo::bar();
}
use it in your view {{my_slug($collection->value)}} or in your controllers with the namespacing App\customs\my_slug($value) or by adding use \App\customs to the top of your controller and then my_slug($value)

Laravel 5 Middleware

Hi I am using Middleware of Laravel 5 and what I am doing is I have middleware which is being used in few controllers, and each of my controllers has it's own configs, which I need to pass my middleware, how can I do that?
If you are using this Middleware in "Stack" (Defined at App/Http/Kernel.php in $middleware array), you don't have the $request->route() available.
BUT, if you are using it on routes.php file as middleware of a single route or group of routes, you can do this way:
Create a config file as you want, with config items named with same name of each route, then in your middleware you get route name and load proper configs.
Sorry if it's confuse, imagine this code:
Config/permissions.php
<?php
return [
// Route name here
'user.index' =>
// Your route options here
[
'option_a' => true,
'option_b' => false,
],
'user.edit' =>
[
'option_a' => true,
'option_b' => true,
],
/* ... */
];
?>
So in your middleware handle function you do something like this:
public function handle($request, Closure $next)
{
$route = $request->route()->getName();
/* Check if there is a permission to this route in config file */
if (\Config::get("permissions.{$route}")) {
$options = \Config::get("permissions.{$route}");
/* Here will be your options by route */
/* Now it's up to you do what you want */
} else {
/* If not, use default options */
}
return $next($request);
}
BTW, I recommend you using Laracast Discuss foruns:
www.laracast.com/discuss

Resources