ApiResource attribute gives Compile Error: Constant expression contains invalid operations - api-platform.com

I'm trying to expose only some endpoints with API Platform as explained here: https://api-platform.com/docs/v2.7/core/operations/.
If I just use the ApiResource attribute, I get the expected result (i.e. the default CRUD endpoints).
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\MyclassRepository;
#[ORM\Entity(repositoryClass: MyclassRepository::class)]
#[ApiResource]
class Myclass
{
}
But none of the options to show only some operations work.
This one:
#[ApiResource(operations=[
new Get(),
new GetCollection()
])]
... just outputs "No operations defined in spec!" on /api/docs. It also makes VSCode angry about "operation=":
Expression is not writable.intelephense(1024)
Undefined constant 'App\Entity\operations'.intelephense(1011)
Syntax error: unexpected token '='PHP(PHP2014)
This one:
#[ApiResource(
operations: [
new Get(),
new GetCollection()
]
)]
... produces the error "Compile Error: Constant expression contains invalid operations".
The project is running locally on Docker php:8.0-fpm with "api-platform/core": "^2.7".
The appropriate "use" statements are present.
I tried different combinations of methods and config (e.g. uriTemplate).
I also tried using api-platform ^2.6 with :
#[ApiResource(
collectionOperations: ['get'],
itemOperations: ['get'],
)]
... which produces the error "Unknown named parameter $collectionOperations".
What am I missing?
Thanks!!

API Platform requires PHP 8.1, not 8.0. After upgrading, this works:
#[ApiResource(
operations: [
new Get(),
new GetCollection()
]
)]

Related

Target class [Agent\App\Http\Controllers\PropertyController] does not exist when I put it inside the route group

I have this route in web.php
Route::group(['prefix'=>'agent','namespace'=>'Agent','middleware'=>
['auth','agent'],'as'=>'agent.'], function()
{
Route::get('/dashboard',[AgentController::class, 'index'])->name('dashboard');
Route::resource('/properties', PropertyController::class);
});
When I run the command below,
php artisan route:list
I got this error:
Illuminate\Contracts\Container\BindingResolutionException
Target class [Agent\App\Http\Controllers\PropertyController] does not
exist.
at
C:\xampp\htdocs\sweethomeFinal\vendor\laravel\framework\src\Illuminate\Container\Container.php:879
875▕
876▕ try {
877▕ $reflector = new ReflectionClass($concrete);
878▕ } catch (ReflectionException $e) {
879▕ throw new BindingResolutionException("Target class [$concrete] does
not exist.", 0, $e);
880▕ }
881▕
882▕ // If the type is not instantiable, the developer is attempting to resolve
883▕ // an abstract type such as an Interface or Abstract Class and
there is
1 [internal]:0
Illuminate\Foundation\Console\RouteListCommand::Illuminate\Foundation\Console{closure}(Object(Illuminate\Routing\Route))
2
C:\xampp\htdocs\sweethomeFinal\vendor\laravel\framework\src\Illuminate\Container\Container.php:877
ReflectionException::("Class Agent\App\Http\Controllers\PropertyController does not exist")
But when I put the "Route::resource('/properties', PropertyController::class);" outside the auth
Route::group(['prefix'=>'agent','namespace'=>'Agent','middleware'=>
['auth','agent'],'as'=>'agent.'], function()
{
Route::get('/dashboard',[AgentController::class, 'index'])->name('dashboard');
});
Route::resource('/properties', PropertyController::class);
It just shows all the route lists. But I wanted to put it inside the auth, may I know what is wrong?
Group namespaces made sense pre-Laravel 8, but now with the suggested way of defining routes as Controller::class the prefixes are basically useless.
Routing before Laravel 8
Before v8, Laravel used a default prefix defined in RouteServiceProvider of App\Http\Controllers\. This meant that you only needed to provide the last part - MyController and it was automatically built out to the fully qualified class name (App\Http\Controllers\MyController).
Routing beginning in v8
In v8, the default controller path was removed ($namespace = null), meaning you have to provide the fully qualified class name yourself, or add the prefix back to the service provider. The most efficient way to do this is using the ::class syntax which returns the required name. This method of providing the class name is also more IDE friendly, which was one of the main reasons for the switch.
The problem with route group namespaces
In the older way of building out the controller class name, the group namespaces were useful for sub folders in your controllers folder.
The path would get built out like:
{default_prefix} + {group_namespace} + {controller name}
Yielding:
App\Http\Controllers\ + Agent\ + PropertyController.
This is actually still how it works in version 8; however, you're providing the values in a different way:
(null) + Agent + App\Http\Controllers\PropertyController, which doesn't make the right path.
Summary
When using the ::class syntax for Laravel routes, group level namespace prefixes really don't make sense to use anymore.
If you browse the versions of the Laravel documentation, you'll also notice that the usage of group namespaces present in version 7 is not included in the version 8 docs, because even though it still "works", it's not very useful.
when you set namespace for a route group, all the routes in this group take that namespace as prefex for it's name.
remove ,'namespace'=>'Agent' form the group definition, it should solve it.
see laravel doc for more details.
your are using laravel 8. add the below line on your web.php .
use App\Http\Controllers\yourcontrollername;

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 fix null given error in laravel-dump-server?

I added "beyondcode/laravel-dump-server": "^1.2"
to my Laravel 5.7 application and sometimes I got error
[2018-10-18 03:44:02] local.ERROR: Argument 1 passed to Symfony\Component\VarDumper\Dumper\HtmlDumper::dump() must be an instance of Symfony\Component\VarDumper\Cloner\Data, null given, called in /mnt/_work_sdb8/wwwroot/lar/Votes/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php on line 47 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Argument 1 passed to Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper::dump() must be an instance of Symfony\\Component\\VarDumper\\Cloner\\Data, null given, called in /mnt/_work_sdb8/wwwroot/lar/Votes/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php on line 47 at /mnt/_work_sdb8/wwwroot/lar/Votes/vendor/symfony/var-dumper/Dumper/HtmlDumper.php:111)
[stacktrace]
I run server in my console as :
php artisan dump-server --format=html > public/dump.html
I added wrapper method to commom trait of my app:
<?php
namespace App\Http\Traits;
use File;
use Barryvdh\Debugbar\Facade as Debugbar;
use Carbon\Carbon;
use Config;
use Intervention\Image\Facades\Image as Image;
trait funcsTrait
{
public function d($data)
{
if (empty($data)) {
return;
}
dump($data);
}
and calling this method in my control :
$this->d('ProfilePageTest Test51:: $newUserSessionData::' . print_r($newUserSessionData, true));
And sometimes I got error described above.
In my wrapper I tried to exclude calling of dump with empty value, supposing that empty value could be reason of this error ?
But looks like the reason was different. If there is a way make work it properly?
Thanks!

Symfony and Swift Mailer

https://swiftmailer.symfony.com/docs/introduction.html
Documents are lacking some information, like all symfony documents are but my problem at moment is that how to instantiate the new Swift_SmtpTransport()?
I cannot find namespace for it. If i do like the document says, i get this error -> Attempted to load class "Swift_SmtpTransport" from namespace App\Controller.
plus, documents don't say how to instantiate Swift_Message class either.
Trying container hasn't worked for me, like so -> $this->container->get
I think the answer is here if you want to send a message -> Attempted to call an undefined method named "newInstance" of class "Swift_Message"
I quote :
Now it is:
$message = \Swift_Message::newInstance()->setSubject('Hello Email')
instead of
$message = \Swift_Message::newInstance()
->setSubject('Hello Email')

Syntax error in server not on localhost

I have this link:
List of all members
and this route:
Route::get('/list', 'NyfnController#list');
controller method:
public function list()
{
$users=User::orderBy('district_involved')->get();
return view('list')->with('users',$users);
}
But, i got the syntax error:
syntax error, unexpected 'list' (T_LIST), expecting identifier
(T_STRING)
This works fine on localhost, but not on server.
Probably your localhost is running 5.6.4> and your webserver is running 7.*.
In php 7 the list method is not available. If you use PHPStorm you got a notice that list is a new method in PHP 7 (or newer). Have a look at: http://php.net/manual/en/function.list.php#refsect1-function.list-changelog
I would recommend you to change you method:
public function listUsers()
{
$users=User::orderBy('district_involved')->get();
return view('list')->with('users',$users);
}
Route::get('/list', 'NyfnController#listUsers');
It happens that list is a reserved word (http://php.net/manual/en/function.list.php), actually a language construct, and therefore you cannot define a function with that name. Use whatever other (not reserved) name you want.

Resources