Our First ApiResource

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Question: have you ever gone to the store and accidentally bought too much cheese? It's the story of my life. Or maybe you have the opposite problem: you're hosting a big party and you don't have enough cheese! This is our new billion dollar idea: a platform where you can sell that extra chunk of Brie you never finished or buy a truck-load of camembert from someone who go a little too excited at the cheese market. Yep, it's what the world is asking for: a peer-to-peer cheese marketplace. We're calling it: Cheese Whiz.

For the site, maybe we're going to make it an single-page application built in React or Vue... or maybe it'll be a little more traditional: a mixture of HTML pages and JavaScript that makes AJAX requests. And maybe we'll even have a mobile app. It doesn't really matter because all of these options mean that we need to be able to expose our core functionality via an API.

Generating the Entity

But to start: forget about the API and pretend like this is a normal, boring Symfony project. Step 1 is... hmm, probably do create some database entities.

Let's open up our .env file and tweak the DATABASE_URL. My computer uses root with no password... and how about cheese_whiz for the database name.

33 lines .env
... lines 1 - 30
DATABASE_URL=mysql://root:@127.0.0.1:3306/cheese_whiz
... lines 32 - 33

You can also create a .env.local file and override DATABASE_URL there. Using root and no password is pretty standard, so I like to add this to .env and commit it as the default.

Cool! Next, at your terminal, run

composer require maker --dev

to get Symfony's MakerBundle... so we can be lazy and generate our entity. When that finishes, run:

php bin/console make:entity

Call the first entity: CheeseListing, which will represent each "cheese" that's for sale on the site. Hit enter and... oh! It's asking:

Mark this class as an API Platform resource?

MakerBundle asks this because it noticed that API Platform is installed. Say "yes". And before we add any fields, let's go see what that did! In my editor, yep! This created the usual CheeseListing and CheeseListingRepository. Nothing special there. Right now, the only property the entity has is id. So, what did answering "yes" to the API Platform resource question give us? This tiny annotation right here: @ApiResource.

... lines 1 - 7
/**
* @ApiResource()
... line 10
*/
class CheeseListing
... lines 13 - 111

The real question is: what does that activate? We'll see that soon.

But first, let's add some fields. Let's see, each cheese listing probably needs a title, string, 255, not nullable, a description, which will be a big text field, price, which I'll make an integer - this will be the price in cents - so $10 would be 1000, createdAt as a datetime and an isPublished boolean. Ok: good start! Hit enter to finish.

Congratulations! We have a perfectly boring CheeseEntity class: 7 properties with getters and setters.

... lines 1 - 11
class CheeseListing
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
/**
* @ORM\Column(type="text")
*/
private $description;
/**
* @ORM\Column(type="integer")
*/
private $price;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @ORM\Column(type="boolean")
*/
private $isPublished;
... lines 45 - 109
}

Next, generate the migration with:

php bin/console make:migration

Oh! Migrations isn't installed yet! No problem, follow the recommendation:

composer require migrations

But before we try generating it again, I need to make sure my database exists:

php bin/console doctrine:database:create

And now run make:migration:

php bin/console make:migration

Let's go check that out to make sure there aren't any surprises:

CREATE TABLE cheese_listing...

... lines 1 - 12
final class Version20190508193750 extends AbstractMigration
{
... lines 15 - 19
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE cheese_listing (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, description LONGTEXT NOT NULL, price INT NOT NULL, created_at DATETIME NOT NULL, is_published TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
}
... lines 27 - 34
}

Yea! Looks good! Close that and run:

php bin/console doctrine:migrations:migrate

Say Hello to your API!

Brilliant! At this point, we have a completely traditional Doctrine entity except for this one, @ApiResource() annotation. But that changes everything. This tells API Platform that you want to expose this class as an API.

Check it out: refresh the /api page. Woh! Suddenly this is saying that we have five new endpoints, or "operations"! A GET operation to retrieve a collection of CheeseListings, a POST operation to create a new one, GET to retrieve a single CheeseListing, DELETE to... ya know... delete and PUT to update an existing CheeseListing. That's a full, API-based CRUD!

And this isn't just documentation: these new endpoints already work. Let's check them out next, say hello to something called JSON-LD and learn a bit about how this magic works behind the scenes.

Leave a comment!

  • 2020-07-02 hanene

    thank you :)))

  • 2020-07-02 Victor Bocharsky

    Hey Hanene,

    Yes, PATCH is another valid operation that allow to apply a partial modification to an element. Unfortunately, we haven't talked about it in this course, but you can read more about it in docs: https://api-platform.com/do...

    I hope this helps!

    Cheers!

  • 2020-07-01 hanene

    Hi everyone I have 6 operations the new one is Patch !!

  • 2020-06-11 Sebastian Romero

    Thanks.

  • 2020-06-08 Vladimir Sadicov

    Hey Sebastian Romero

    Yep the error is in command, there shouldn't be a dot before bin use just
    bin/console make:entity or php bin/console make:entity

    Cheers!

  • 2020-06-06 Sebastian Romero

    hi, i have a problem.
    .bin/console make:entity
    ".bin" no se reconoce como un comando interno o externo,
    programa o archivo por lotes ejecutable.

    when i execute .bin/console make:entity

  • 2020-05-14 Diego Aguiar

    Hey Mustafa Yousef

    Which MySql version are you using? Can you try creating a new MySql user and use it on the connection? I would like to see your DB connection URL
    Also, if you pass in the flag -vvv to the command, you'll get a bunch of debugging info that may help you out understanding what's going on.

    Cheers!

  • 2020-05-14 Mustafa Yousef

    Hi,

    when I run >php bin/console doctrine:database:create, I always get a SQLSTATE[HY000] [2002] connection refused error.
    That way I can't create the database. I used the exact same root user without a password. Am I missing something?

    Cheers!

  • 2020-04-22 Vladimir Sadicov

    Hey Julien Bonnier

    Just wanted to say that your report helped to fix maker-bundle, so WOOHOO it's working now!!!

    PS It also helped to find another bug in symfony ;)

    Cheers! Have a nice day!

  • 2020-04-21 Vladimir Sadicov

    You are welcome! It was pretty easy...This course should work without errors, and I knew that maker-bundle was updated some time ago, so I just checked it manually)) However we are still investigating this issue because it should work with new bundle as well

    Cheers!

  • 2020-04-21 Julien Bonnier

    Works like a charm! Thanks man.

    How did you determined that the maker bundle was the problem?

  • 2020-04-21 Vladimir Sadicov

    Hey Julien Bonnier

    Woh we a very sorry that you faced this issue, the reason is new maker-bundle bundle, to be able to continue course I'll advise to downgrade maker to version 1.14.6 you can do it with composer require symfony/maker-bundle:1.14.6 --dev oh and you need to delete your Entity and repository before downgrading, after create it again and everything will work.

    Hope it will help, again sorry for this issue!

    Cheers!

  • 2020-04-21 Julien Bonnier

    Hi!

    I just started this course and ran into an error.


    Executing script cache:clear [KO]
    [KO]
    Script cache:clear returned with error code 1
    !!
    !! In DefinitionErrorExceptionPass.php line 54:
    !!
    !! Cannot autowire service "App\Repository\CheeseListingRepository": argument
    !! "$registry" of method "__construct()" references interface "Doctrine\Persis
    !! tence\ManagerRegistry" but no such service exists. You should maybe alias t
    !! his interface to the existing "doctrine" service.
    !!
    !!
    !!
    Script @auto-scripts was called via post-update-cmd

    Installation failed, reverting ./composer.json to its original content.

    While trying to intstall migrations. For some reason I'm missing some autowiring.

    Help please!

  • 2020-02-28 Skylar Scotlynn Gutman

    Thanks for replying. That worked!

  • 2020-02-28 Victor Bocharsky

    Hey Skylar,

    It can be easily configured! Change this line in your "config/routes/api_platform.yaml":
    https://github.com/symfony/...

    Well, of course if you have a custom /api URL, not handled by API Platform already :) So, yeah, your solution sounds good to me.

    Cheers!

  • 2020-02-28 Skylar Scotlynn Gutman

    Thanks for another great tutorial.

    Does the base path have to be /api or can that be changed? I would like it to be /apiv2 or something. I ask because my app already has enpoints with /api and I would like to gradually move to the api-platform. What pitfalls can I avoid when installing api-platform and it's modification.

  • 2019-09-05 Diego Aguiar

    Awesome!

  • 2019-09-05 Annemieke Buijs

    Thank you Diego, it works. I've created a folder for pms database and portal database


    entity_managers:
    portal:
    connection: portal
    mappings:
    App:
    is_bundle: false
    type: annotation
    dir: '%kernel.project_dir%/src/Entity/Portal'
    prefix: 'App\Entity\Portal'
    alias: App

    pms:
    connection: pms
    mappings:
    App:
    is_bundle: false
    type: annotation
    dir: '%kernel.project_dir%/src/Entity/Pms'
    prefix: 'App\Entity\Pms'
    alias: App

  • 2019-09-04 Diego Aguiar

    Hey Annemieke Buijs

    I think the problem relies on your entity managers config. At first sight of your config code I can see that the dir option of your managers is wrong. Your entities should be isolated into their own folder. I recommend you to read this piece of documentation https://symfony.com/doc/cur... but if you have more questions feel free to ask us :)

    Cheers!

  • 2019-09-04 Annemieke Buijs

    You're fast. Thank you!
    I've decided to create an entity for the existing db table. This works great.
    But now I run into a different 'challenge'. When using api platform multiple database connections won't work.

    This is my product entity which dbtable is on pms database

    /**
    * @ApiResource(
    * collectionOperations={"get"},
    * itemOperations={"get"}
    * )
    * @ORM\Table(name="pms.pms_product")
    * @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
    */

    class Product
    {
    /**
    * @ORM\Column(name="id", type="integer")
    * @ORM\Id
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    private $id;

    This is my article entity which is on portal database

    /**
    * @ApiResource()
    * @ORM\Table(name="portal.article")
    * @ORM\Entity(repositoryClass="App\Repository\Article\ArticleRepository")
    */
    class Article
    {
    /**
    * @ORM\Column(name="id", type="integer")
    * @ORM\Id
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    private $id;

    This is my doctrine.yaml
    doctrine:
    dbal:
    default_connection: pms
    connections:
    portal:
    url: '%env(resolve:DATABASE_PORTAL_URL)%'
    driver: 'pdo_mysql'
    charset: utf8mb4
    default_table_options:
    charset: utf8mb4
    collate: utf8mb4_unicode_ci
    pms:
    url: '%env(resolve:DATABASE_PMS_URL)%'
    driver: 'pdo_mysql'



    orm:
    auto_generate_proxy_classes: true
    default_entity_manager: pms

    entity_managers:
    portal:
    connection: portal
    mappings:
    App:
    is_bundle: false
    type: annotation
    dir: '%kernel.project_dir%/src/Entity'
    prefix: 'App\Entity'
    alias: App

    pms:
    connection: pms
    mappings:
    App:
    is_bundle: false
    type: annotation
    dir: '%kernel.project_dir%/src/Entity'
    prefix: 'App\Entity'
    alias: App

    If i remove portal connection from doctrine.yml the product calls in api platform work just fine.
    When I put back the connection data for the portal, it will query the portal database.
    I know how to fix this in a 'normal' application, but within the apiplatform i don't know how to do this. Can you help me out?

  • 2019-09-03 weaverryan

    Hey Annemieke!

    Great question! And yes, you *can* do all of this, even if you don't have entities. It's a more advanced use-case and some things won't work unless you do a bit more work (e.g. pagination), but getting the basic set up is actually not too hard. What you'll need is: a (A) custom data persister and (B) custom data provider. You *will* need a PHP class to represent each API resource, but it doesn't need to be tied to Doctrine in any way. If you put these in the src/Entity directory, you won't even need to change the API Platform configuration to look in another directory (but if the classes live elsewhere, it's just a simple config change). The biggest "gotcha" i know about relates to pagination, filtering, etc. All that stuff works out-of-the-box with Doctrine. But if *you* are taking responsibility for querying for data via the data persister, then you need to be responsible for this step too. The docs about some info about this https://api-platform.com/do...

    Let me know how it goes!

    Cheers!

  • 2019-09-02 Annemieke Buijs

    Thank you for these great tutorials on symfonycasts. They realy helped me do a better job as a programmer.
    I have a question.
    Is there a way to use api platform, openapi etc. without entities. We have a lot of data stored at many different places and NO entities. I am using all this data in a non-api program made with symfony 3. I have repositories that query the data, no problem. But i'm wondering how to use this data within an api and with the help of t his wonderfull apiplatform. Thank you!!

  • 2019-06-12 Sung Lee

    Hey weaverryan .
    Thanks for the quick reply. That's sad that there is no magic 🎩 like when you create a new entity.

  • 2019-06-12 weaverryan

    Hey Sung Lee!

    Glad you like it ❤️

    It's not a perfect process, but here's how you can import from an existing database https://symfony.com/doc/cur...

    You'll need to add the @ApiResource annotation by hand after finishing... but that should be it ;)

    Cheers!

  • 2019-06-12 Sung Lee

    This is awesome! Creating an entity with a few steps of commands blows my mind.
    One question though. How can I create an entity with an existing table?
    When I run ./bin/console doctrine:mapping:import "App\Entity" annotation --path=src/Entity, it tries to create all table entities and rewrites existing entities. Also it doesn't generate `@ApiResource()` annotation.
    My question is:
    - How can I generate an entity for a specific existing table with api resource annotation?

    Thank you.