How to edit picture on Coudinary with Laravel - laravel

Any Idea how to edit pictures on Cloudinary using Laravel API? I did a lot of searches, but I didn't find any references. The add worked successfully, but I didn't find a solution for the edit.
Add code
$user->user_image = Cloudinary::upload(
$request->file('user_image')->getRealPath(),
[
'folder' => 'Testing'
]
)->getSecurePath();
Attempt at updating picture
public function updatePicture(Request $request, $user_id)
{
$data = $request->validate([
'user_image' => '',
]);
$data = Cloudinary::upload(
$request->file('user_image')->getRealPath(),
[
'folder' => 'Testing'
]
)->getSecurePath();
User::where("user_id", $user_id)->update($data);
return response()->json([
'message' => 'Successfully updated Picture!',
'success' => true,
], 200);
}

For deleting, you can use the destroy() method of the API, for example:
$result = Cloudinary::destroy(
$public_id, //Public ID of the file from Cloudianry to delete - returned from the Upload API response
[
'...' => '...' //any optional parameters
]
)
For a list of all optional parameters and possible values please see:
https://cloudinary.com/documentation/image_upload_api_reference#destroy_method
In terms of updating, I am assuming you are referring to Cloudinary's update details of a single resource method of the Admin API. If so, you can access it like so:
$admin_api = Cloudinary::admin();
$result = $admin_api->update($public_id, $options = []);
Alternatively, if you're referring to the explicit method of the Upload API then you could access it like so:
$result = Cloudinary::explicit($public_id, $options = []);

Related

Path of the file stored in s3 does not match with provided - Using Laravel

I'm building a service to upload images with laravel and stored in a aws s3 bucket, this is the function responsible for store image.
public function fromUrl(Request $request)
{
$validator = Validator::make($request->all(), [
'files' => 'required|array|min:1',
'files.*' => 'string',
]);
if (!$validator->fails()) {
$paths = [];
foreach ($validator->validate()['files'] as $file) {
$url = config('services.s3.host') . Storage::disk('s3')->put('images/public', file_get_contents($file), 'public');
array_push($paths, $url);
}
return $paths;
} else {
throw new ValidateException([
'message' => $validator->errors()->toArray(),
'rules' => $validator->failed()
]);
}
}
The request body looks like this.
{
"files": [
"https://image-url-1",
"https://image-url-2"
]
}
I expect that the path returned when saving the image is something like this.
[
"https://my-bucket-url/images/public/random-name-for-image1",
"https://my-bucket-url/images/public/random-name-for-image2"
]
but instead I'm getting the following.
[
"https://my-bucket-url/1",
"https://my-bucket-url/1"
]
You are misusing put in your example.
Firstly the first parameter is the path plus filename and you have no filename random logic. The third parameter is options array.
$randomFileName = uniqid(rand(), true);
$path = 'images/public/' . $randomFileName;
Storage::disk('s3')->put($path, file_get_contents($file));
This code will save an element at images/public/$randomFileName. To return the proper path you can use the url() method.
$url = Storage::disk('s3')->url($path);
array_push($paths, $url);

Additional data in Laravel Resource

I use Laravel resource from the controller:
$data = Project::limit(100)->get();
return response()->json(ProjectResource::collection($data));
I like to pass additional information to the ProjectResource. How it's possible? And how can i access the additional data?
I tried like this:
$data = Project::limit(100)->get();
return response()->json(ProjectResource::collection($data)->additional(['some_id => 1']);
But it's not working.
What's the right way?
I like to access the some_id in the resource like this.
public function toArray($request)
{
return [
'user_id' => $this->id,
'full_name' => $this->full_name,
'project_id' => $this->additional->some_id
];
}
In your controller don't wrap return Resource in response()->json.
Just return ProjectResource.
So like:
$data = Project::limit(100)->get();
return ProjectResource::collection($data)->additional(['some_id => 1']);
Sorry for misunderstanding the question.
I don't think there is an option to pass additional data like this. So you will have to loop over the collection and add this somehow.
One option is to add to resources in AnonymousCollection. For example:
$projectResource = ProjectResource::collection($data);
$projectResource->map(function($i) { $i->some_id = 1; });
return $projectResource;
and then in ProjectResource:
return [
'user_id' => $this->id,
'full_name' => $this->full_name,
'project_id' => $this->when( property_exists($this,'some_id'), function() { return $this->some_id; } ),
];
Or add some_id to project collection befour passing it to ResourceCollection.

How to get paymentMethodNonce in Braintree API?

I'm working in laravel 5.4
My transactions are successfull when I try a 'fake_nonce' type of string provided by the braintree docs. But when I tried to get the paymentMethodNonce it always gives me error like nonce not found. And sometimes http error!!! If I try to configure it by myself!
Take a look at my controller function below
public function addOrder(Request $request){
$customer = Braintree_Customer::create([
'firstName' => $request->guest_name,
'email' => $request->guest_email,
'phone' => $request->guest_phone
]);
$customer->success;
$customer->customer->id;
$find = Braintree_Customer::find($customer->customer->id);
$nonceFromTheClient = Braintree_PaymentMethodNonce::find($find);
$result = Braintree_Transaction::sale([
'amount' => $request->subtotal,
'paymentMethodNonce' => $nonceFromTheClient,
'options' => [
'submitForSettlement' => True
]
]);
if ($result->success) {
$settledTransaction = $result->transaction;
} else {
print_r($result->errors);
}
Cart::destroy();
return view('guest/track', compact('result'));
}
$nonceFromTheClient = Braintree_PaymentMethodNonce::find($find);
Your using the wrong nonce, this nonce must come from the DropIn ui and not be generated on your code.
Please check the onPaymentMethodReceived() method provided in the JS SDK.
Please check this reference

Better way for testing validation errors

I'm testing a form where user must introduce some text between let's say 100 and 500 characters.
I use to emulate the user input:
$this->actingAs($user)
->visit('myweb/create')
->type($this->faker->text(1000),'description')
->press('Save')
->see('greater than');
Here I'm looking for the greater than piece of text in the response... It depends on the translation specified for that validation error.
How could do the same test without having to depend on the text of the validation error and do it depending only on the error itself?
Controller:
public function store(Request $request)
{
$success = doStuff($request);
if ($success){
Flash::success('Created');
} else {
Flash::error('Fail');
}
return Redirect::back():
}
dd(Session::all()):
`array:3 [
"_token" => "ONoTlU2w7Ii2Npbr27dH5WSXolw6qpQncavQn72e"
"_sf2_meta" => array:3 [
"u" => 1453141086
"c" => 1453141086
"l" => "0"
]
"flash" => array:2 [
"old" => []
"new" => []
]
]
you can do it like so -
$this->assertSessionHas('flash_notification.level', 'danger'); if you are looking for a particular error or success key.
or use
$this->assertSessionHasErrors();
I think there is more clear way to get an exact error message from session.
/** #var ViewErrorBag $errors */
$errors = request()->session()->get('errors');
/** #var array $messages */
$messages = $errors->getBag('default')->getMessages();
$emailErrorMessage = array_shift($messages['email']);
$this->assertEquals('Already in use', $emailErrorMessage);
Pre-requirements: code was tested on Laravel Framework 5.5.14
get the MessageBag object from from session erros and get all the validation error names using $errors->get('name')
$errors = session('errors');
$this->assertSessionHasErrors();
$this->assertEquals($errors->get('name')[0],"The title field is required.");
This works for Laravel 5 +
Your test doesn't have a post call. Here is an example using Jeffery Way's flash package
Controller:
public function store(Request $request, Post $post)
{
$post->fill($request->all());
$post->user_id = $request->user()->id;
$created = false;
try {
$created = $post->save();
} catch (ValidationException $e) {
flash()->error($e->getErrors()->all());
}
if ($created) {
flash()->success('New post has been created.');
}
return back();
}
Test:
public function testStoreSuccess()
{
$data = [
'title' => 'A dog is fit',
'status' => 'active',
'excerpt' => 'Farm dog',
'content' => 'blah blah blah',
];
$this->call('POST', 'post', $data);
$this->assertTrue(Post::where($data)->exists());
$this->assertResponseStatus(302);
$this->assertSessionHas('flash_notification.level', 'success');
$this->assertSessionHas('flash_notification.message', 'New post has been created.');
}
try to split your tests into units, say if you testing a controller function
you may catch valication exception, like so:
} catch (ValidationException $ex) {
if it was generated manually, this is how it should be generated:
throw ValidationException::withMessages([
'abc' => ['my message'],
])->status(400);
you can assert it liks so
$this->assertSame('my message', $ex->errors()['abc'][0]);
if you cannot catch it, but prefer testing routs like so:
$response = $this->json('POST', route('user-post'), [
'name' => $faker->name,
'email' => $faker->email,
]);
then you use $response to assert that the validation has happened, like so
$this->assertSame($response->errors->{'name'}[0], 'The name field is required.');
PS
in the example I used
$faker = \Faker\Factory::create();
ValidationException is used liks this
use Illuminate\Validation\ValidationException;
just remind you that you don't have to generate exceptions manually, use validate method for common cases:
$request->validate(['name' => [
'required',
],
]);
my current laravel version is 5.7

Laravel Socialite - Avatar is to slow

I am using the Laravel 5.0 with the Socialite Library. Everything works fine except i am a little disappointment with the size of the avatar.
Is it possible to get a bigger avatar?
Looking at the source code of Socialite https://github.com/laravel/socialite/blob/2.0/src/Two/FacebookProvider.php
You can see at line 91 that the url for the avatar appends a static ?type=normal at the end. The Facebook graph API documentation says that you can request an avatar size using an ENUM or custom width/height so you can modify line 91 and append an ENUM or custom width/height like ?type=large
More details can be found in the Facebook graph API documentation.
However, this is only for the Facebook driver so you will need to dig in a similar fashion for other providers. If their APIs do not allow such freedom as in the case of Facebook then you will need to do with the avatar being returned.
Updated March 12, 2015
Do not customize the original Socialite package, fork the repository and then make the change. You can then plug your forked repository into your project and also send a pull request to the original author in case he thinks its worth having the functionality you have implemented. Also, your forked repository will not be updated/maintained as is the case with the original package. In case you choose to update to the new package, your changes will be lost.
Fix for Facebook / Google / Twitter Small Avatar Photo
I have created this helper method on my AuthController
public function getBigAvatar($user, $provider)
{
return ($provider == "google") ? $user->getAvatar()."0" : $user->avatar_original;
}
AND THIS IS HOW I CALL IT:
$user = Socialite::driver( $provider )->user();
$userPhoto = $this->getBigAvatar($user, $provider);
So Simple in case its Google, Well just append 0 to the end of the url and well get a 500px avatar. And for twitter and Facebook, The Providers already offers an avatar_original attribute as seen in
FacebookProvider.php
protected function mapUserToObject(array $user)
{
$avatarUrl = $this->graphUrl.'/'.$this->version.'/'.$user['id'].'/picture';
return (new User)->setRaw($user)->map([
'id' => $user['id'], 'nickname' => null, 'name' => isset($user['name']) ? $user['name'] : null,
'email' => isset($user['email']) ? $user['email'] : null, 'avatar' => $avatarUrl.'?type=normal',
'avatar_original' => $avatarUrl.'?width=1920',
]);
}
TwitterProvider.php
return $instance->map([
'id' => $user->uid, 'nickname' => $user->nickname,
'name' => $user->name, 'email' => $user->email, 'avatar' => $user->imageUrl,
'avatar_original' => str_replace('_normal', '', $user->imageUrl),
]);
Since Google does not map this, And gets a default 50px image, we simple change it to 500px with is great for avatar.
WORKED PERFECT FOR ME, LET ME KNOW IF IT HELPS YOU!
Just append your required size after the ?type=normal like this:
graph.facebook.com/v2.2/{user}/picture?type=normal &width=76&height=76
this will override the type=normal
For use original avatars size from Facebook and Google:
public function handleProviderCallback($provider){
$userData = Socialite::driver($provider)->user();
$user = new User;
...
Google:
$user->avatar = preg_replace('/\?sz=[\d]*$/', '', $userData->avatar);
Facebook:
$user->avatar = $userData->avatar_original;
here is another simpler way for the avatar
public function redirectToProvider($provider)
{
return Socialite::driver($provider)->redirect();
}
public function handleProviderCallback($provider)
{
$user = Socialite::driver($provider)->user();
// dd($user);
if ($user) {
$authUser = $this->findOrCreateUser($user, $provider);
Auth::login($authUser, true);
return redirect()->route('home');
}
return 'something went wrong';
}
private function findOrCreateUser($user, $provider)
{
$found = User::where('provider_id', $user->id)->first();
if ($found) {
return $found;
}
// so the default is G+, change according to your needs
$avatar = str_replace('?sz=50', '', $user->avatar);
if ($provider == 'facebook' || $provider == 'twitter') {
$avatar = $user->avatar_original;
}
return User::create([
'username' => $user->name,
'email' => $user->email,
'avatar' => $avatar,
'provider' => $provider,
'provider_id' => $user->id,
]);
}
this way u dont change ur code much and easier for maintenance ,
however for some reason the facebook avatar doesnt show up in my app , plz if anyone can help i would deeply appreciate it.
here is my the current fb avatar link i get
https://graph.facebook.com/v2.6/xxxxxxxxxxx/picture?width=1920
and the view
<img class="user-avatar" src="{{ $user->avatar }}" alt="user avatar">
Here is code I have came up with
if($file = $user->getAvatar()) {
if ($provider == 'google') {
$file = str_replace('?sz=50', '', $file);
} elseif ($provider == 'twitter') {
$file = str_replace('_normal', '', $file);
} elseif ($provider == 'facebook') {
$file = str_replace('type=normal', 'type=large', $file);
}
}
Happy coding everyone :)
This is basically just replacement or a part of avatar image URL so it would return the bigger one.
getAvatar() function return the image url, so I stored it in variable $file
And depending on the provider url structure is different, so for each provider you need to change image URL string accordingly
Google
remove '?sz=50' from image url string
Twitter
remove '_normal' from image url string
Facebook
replace 'type=normal' with 'type=large' in image url string

Resources