I am trying to add a profile image feature in my app. I am using the TDD approach to do this. The test for uploading profile picture shows green. But when I run the test to update the profile picture, it gives an error. Below is the code:
Controller:
public function store(StoreAvatarRequest $request)
{
$path = $request->file('avatar')->store('avatars');
BasicInformation::updateOrCreate([
'user_id' => auth()->id(),
], [
'user_id' => auth()->id(),
'avatar' => $path,
]);
$this->showAlerts('alert-success', 'Profile Image uploaded successfully');
return redirect()->route('avatar.index');
}
public function update(StoreAvatarRequest $request, $id)
{
$basicProfile = BasicInformation::find($id)->first();
$oldAvatarPath = $basicProfile->avatar;
if ($basicProfile->user_id == auth()->id()) {
$path = $request->file('avatar')->store('avatars');
$basicProfile->avatar = $path;
$basicProfile->update();
Storage::delete($oldAvatarPath);
$this->showAlerts('alert-success', 'Profile Image Updated Successfully');
return redirect()->route('avatar.index');
} else {
// TODO :: Need to add logic here
}
}
Test Case:
public function can_update_profile_picture()
{
$this->actingAs($this->lawyer)->post(route('avatar.store'), [
'avatar' => UploadedFile::fake()->image('avatar.jpg', 600, 600)
]);
$oldImagePath = $this->lawyer->information->avatar;
$this->actingAs($this->lawyer)->put(route('avatar.update', ['id' => $this->lawyer->information->id]), [
'avatar' => UploadedFile::fake()->image('avatar1.jpg', 600, 600)
])
->assertRedirect(route('avatar.index'))
->assertSessionHas('status', 'Profile Image Updated Successfully');
Storage::disk('local')->assertExists($this->lawyer->information->avatar);
Storage::disk('local')->assertMissing($oldImagePath);
}
I am getting the following error when I run the test:
PHPUnit 5.7.19 by Sebastian Bergmann and contributors.
F 1 /
1 (100%)
Time: 298 ms, Memory: 20.00MB
There was 1 failure:
1) Tests\Feature\Dashboard\Lawyer\Account\ProfileImageTest::can_update_profile_picture
Unable to find a file at path [local/c5tjUUQDzU4iKHauJK68Z801I5iaYJ7e3cVQ5iA1.jpeg].
Failed asserting that false is true.
This is a matter of configuration and then preference for using store() or storeAs() methods.
First off you have to configure filesystems.phpto recognize your tests Storage disk as the fake() disk:
'testing' => [
'driver' => 'local',
'root' => storage_path('framework/testing/disks/'),
'visibility' => 'public',
Using that setup allows you to use your APP_ENV value in phpunit.xmlfor defaulting this disk for your tests.
Also, I use the use the storeAs()in my controller so that I can test against the filename I stored in the assertExists() method in my test.
$request->file('avatar')->storeAs('avatar', $request->file('avatar')->getClientOriginalName())
Storage::disk('avatar')->assertExists('avatar.jpg');
This is what works for me and the files are deleted before running each test and clean up is not an issue. You can also do a test for the hashName and use the store() method by getting the responsed and using baseName() to get the name of the new file.
I have added to phpunit.xml next line:
<env name="FILESYSTEM_DRIVER" value="uploads"/>
and while checking
$image = $response->original['logo']; // as the image name get changed in Controller
Storage::disk('uploads')->assertExists("public/uploads/$image");
Others codes are similar as Laravel Doc.
You may Like to check the discussion at Laracast.
Related
i have table 'case' that contains an image column, i want users to insert into the case table and be able to upload an image, on the home page users can view cases along with image of each case.
so far i have tried:
public function createcase(Request $request){
$validator = Validator::make($request->all(), [
'title' => 'required',
'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
'description' => 'required',
'caseTypeId' => 'required',
'amountneeded' =>'required',
'uid' => 'required',
'visible' => 'required',
]);
if ($validator->fails()) {
return response()->json(['error'=>$validator->errors()], 401);
}
$image = $request->file('image');
$path = $image->store('public');
$case = casee::create([
'title' => $request->title,
'description' => $request->description,
'caseTypeId' => $request->caseTypeId,
'amountneeded' => $request->amountneeded,
'uid' => $request->uid,
'visible' => $request->visible,
'image' => $path,
]);
return response()->json(['image' => $image]);
to upload:
<input type="file" id="image" #change="onFileChange" />
methods: {
onFileChange(e) {
this.image = e.target.files[0];
},
async postcase() {
const formData = new FormData();
formData.append('title', this.title);
formData.append('description', this.description);
formData.append('amountneeded', this.amountneeded);
formData.append('caseTypeId', this.selectedcasetype);
formData.append('uid', this.uid);
formData.append('visible', 1);
formData.append('image', this.image);
try {
await axios.post('http://127.0.0.1:8000/api/postcase', formData).then(response => {
console.log(response.data);
})
} catch (error) {
console.log(response.data);
}
}
to retrieve:
<v-flex class="ma-2" v-for="casee in cases" :key="casee.id" lg3 xs12 md6 sm4>
<img v-if="casee.image" :src="'/storage/' + casee.image" alt="Case Image">
mounted(){
this.getcases();
},
methods:{
async getcases(){
try {
const response = await axios.get('http://127.0.0.1:8000/api/cases');
this.cases = response.data.cases;
console.log(this.cases);
} catch (error) {
console.log(error);
}
},
everything works fine except for the image. it returns the error Cannot GET /storage/public/CQu7g4X98APkiMSbCGlqyiJhaXzLaEk8P0pZXaD3.jpg when i try to retrieve it
Frontend couldn't access filesystem as "/storage/public/FILENAME.jpg".
If you are based on Laravel 9.x, and didn't change any filesystem default settings, your file will stored under "<project root>/storage/app/public/", it's filesystem default path, but frontend can only access your "<project root>/public/" folder, do check list as below might help you slove issue.
1.Make sure you executed php artisan storage:link command, and it will created a symbolic link from your storage path to public path.
If you didn't change settings, it will create link from "<project root>/storage/app/public" to "<project root>/public/storage", once you done command you can see your storage folder under public folder.
You can access "<APP_URL>/storage/FILENAME.jpg" to see if link create success or not, if you ain't get 404 means your link created as well as it should be.
2. Use laravel helper to generate storage url.
Use the asset() helper create a URL to the files.
Your files will under "<project root>/public/storage" after step 1, and your nginx root will access public as default, so you don't need public as your url path anymore.
asset('storage/FILENAME.jpg');
// output will be "<APP_URL>/storage/FILENAME.jpg"
3. (Not required) Check if laravel cached your view.
For some new developer of laravel didn't notice that laravel will cached your view, and they keep thinking that code doesn't work.
You can easily check if laravel cached view by php artisan about command if you are using latest laravel version, and use php artisan view:clear to clear view cache.
Make sure that the storage directory is correctly linked to the public.
You can run this command to make it automatically:
php artisan storage:link
heres how i solved it if anyone comes across this issue :
based on the previous answers:
1. php artisan storage:link
replace 'image' => $path to 'image' => basename($path).
3.<img :src="'http://127.0.0.1:8000/storage/' + casee.image" alt="Case Image">
i get the following error when i try to login and i don't find where the problem is. I use lumen 9 with php 8.1.
<!-- Lcobucci\JWT\Token\Builder::relatedTo(): Argument #1 ($subject) must be of type string, null given, called in /home/ss20nt22/public_html/wms/vendor/php-open-source-saver/jwt-auth/src/Providers/JWT/Lcobucci.php on line 212 (500 Internal Server Error) -->
here is my function:
public function login(Request $request)
{
$this->validate($request, [
'nume' => 'required|string',
'password' => 'required|string',
]);
$credentials = $request->only(['nume', 'password']);
if (!$token = Auth::attempt($credentials)) {
// Login has failed
return response()->json(['message' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
In config\jwt.php file Change
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
to
'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class
I solved it as follows, I uninstalled the package and installed it again from the documentation here.
https://jwt-auth.readthedocs.io/en/develop/laravel-installation/
In the case of lumen, do the same procedure with the documentation below.
https://jwt-auth.readthedocs.io/en/develop/lumen-installation/
Steps:
uninstall the package
Remove JWT_SECRET from .env or elsewhere
If possible remove the file
config/jwt.php
Clear all lumen cache
Stop the web service
Follow the installation instructions in the link
https://jwt-auth.readthedocs.io/en/develop/lumen-installation/
Recalling that I use Laravel 8 in php 8
I am accepting a file and some other parameters with it. I validate the file and the parameters and then I store the file while making a record in the database. Pretty standard stuff. The issue I have is that a majority of my files and records get saved but sometimes there exists a record in the database but there is no file associated with it saved. I have tried reproducing it but I haven't been able to. I don't know if the error is my code, or my server or if the user prematurely loses connection or some other issue of that nature.
I am running Laravel 7 on AWS Lightsail instance with Bitnami LAMP stack.
Store Method in Controller
public function store(StoreRequest $request)
{
$filePath = $request
->file('file')
->storeAs(
'path',
Str::upper($request->input('param1')) .
"_{$request->input('param2')}_{$request->input(
'param3'
)}_{$request->input('param4')}_{$request->input(
'param5'
)}_" .
now()->format('Ymd_Hi') .
".{$request->file('file')->getClientOriginalExtension()}",
'public'
);
Storage::setVisibility('public/' . $filePath, 'private');
$record = Model::create(
array_merge($request->all(), ['file' => $filePath])
);
return redirect()
->back()
->with('message', 'File submitted successfully');
}
Rules in StoreRequest
public function rules()
{
$rules = [
//rules for other parameters
'filetype' => ['required', 'string'],
];
if (request('filetype') === 'video') {
$rules['file'] = [
'required',
'file',
'mimetypes:video/*',
'max:200000',
];
} elseif (request('filetype') === 'image') {
$rules['file'] = ['required', 'file', 'image', 'max:20000'];
}
return $rules;
}
I have 259 records on the database but I have only received 247 files. Where could the error lie? I have tried on my development environment but I haven't been able to reproduce an error like this. Is it something to do with my php.ini settings? Is it something that the user is causing? How can the file not be saved when I am saving it before the record gets stored in the database?
You are using Ymd_Hi which would not allow for any records saved in the same minute, perhaps use a timestring or include seconds too, but be warned, if you use seconds you may face the same issue!
There are two options in pho.ini file. One is upload_max_filesize and another one post_max_size. While uploading the file I think it crosses the size that defined in ini file.
If your video size 200 MB then upload_max_size should be more than or equal 200 MB. post_max_size is total size of form that going to be submitted. It is safer to set size more than upload_max_size.
i'm using laravel6, and voyager, i'm creating artisan command for installation use cmd following php artisan make: command EcommerceInstall
, I recopy DataTypesTableSeeder from database/seeder and I rename it by DataTypesTableSeederCustom, I modify my file which I wrote EcommerceInstall.php but it gives me error class [DataTypesTableSeederCustom] does not exist.
i think i forget import the class but i don't know where .
EcommerceInstall.php
public function handle()
{
if ($this->confirm('this well delete all you current data and install the dummy default data, Are you sure ?')) {
File::deleteDirectory(public_path('storage/products/dummy'));
$this->callSilent('storage:link');
$copySuccess = File::copyDirectory(public_path('img/products'),public_path('storage/products/dummy'));
if($copySuccess){
$this->info('images succefully copied to storage folder');
}
$this->call('migrate:fresh', [
'--seed' => true,
]);
$this->call('db:seed', [
'--class' => 'DataTypesTableSeederCustom'
]);
$this->info('Dummy data installed');
}
I built an API using dingo/api 0.10.0, Laravel 5.1 and lucadegasperi/oauth2-server-laravel": "^5.1".
All my routes work fine in Postman/Paw!
The problem appears when I try to test the API using PHPUnit.
This is part of my route-api.php file
<?php
$api = app('Dingo\Api\Routing\Router');
$api->version(['v1'], function ($api) {
$api->post('oauth/access_token', function () {
return response(
\LucaDegasperi\OAuth2Server\Facades\Authorizer::issueAccessToken()
)->header('Content-Type', 'application/json');
});
$api->group(['middleware' => ['oauth', 'api.auth']], function ($api) {
$api->post('/register', 'YPS\Http\Controllers\Api\UserController#register');
});
And this is my test file UserRegistrationTest.php
class UserRegistrationTest extends ApiTestCase
{
public function setUp()
{
parent::setUp();
parent::afterApplicationCreated();
}
public function testRegisterSuccess()
{
$data = factory(YPS\User::class)->make()->toArray();
$data['password'] = 'password123';
$this->post('api/register', $data, $this->headers)
->seeStatusCode(201)
->seeJson([
'email' => $data['email'],
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
]);
}
public function testRegisterMissingParams()
{
$this->post('api/register', [], $this->headers, $this->headers, $this->headers)->seeStatusCode(422);
}
}
The ApiTestCase simply retrieves a token and sets the headers.
private function setHeaders()
{
$this->headers = [
'Accept' => 'application/vnd.yps.v1+json',
'Authorization' => 'Bearer ' . $this->OAuthAccessToken,
];
}
Now, the weird part is that the first test testRegisterSuccess runs perfectly and returns the response I expect. But the second one testRegisterMissingParams, even though it's the same route, returns this,
array:2 [
"message" => "The version given was unknown or has no registered routes."
"status_code" => 400
]
I tracked the error and it is in the Laravel adapter here:
public function dispatch(Request $request, $version)
{
// it seems that the second time around can't find any routes with the key 'v1'
if (! isset($this->routes[$version])) {
throw new UnknownVersionException;
}
$routes = $this->mergeExistingRoutes($this->routes[$version]);
$this->router->setRoutes($routes);
return $this->router->dispatch($request);
}
And further more, if i run one test at a time (eg comment one out, run test and then comment the other and run test) i see the result expected in both tests. The problem is when i run multiple tests.
Any thoughts on that?
Thank you!
Run php artisan api:routes to see full path you may have missed something for the URL, also if this working if you request your URL manually?
I had same problem with testing using Dingo & Lumen. This worked for me - remove bootstrap="bootstrap/app.php" from phpunit.xml file and change line processIsolation="false" to processIsolation="true".