{"id":22257,"date":"2021-01-04T17:17:18","date_gmt":"2021-01-04T17:17:18","guid":{"rendered":"http:\/\/how_to_build_an_enhanced_gravatar_service_part_1"},"modified":"2024-05-27T14:13:07","modified_gmt":"2024-05-27T21:13:07","slug":"how_to_build_an_enhanced_gravatar_service_part_1","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1","title":{"rendered":"How to Build an Enhanced Gravatar Service, Part 1"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>The advent of web development since 25 years ago has given birth to an abundance of online services that help developers build apps efficiently and smoothly. Gravatar is a shining example of such a service. Built by WordPress, Gravatar generates globally recognized avatars. Fun fact: 80 percent of Gravatar users first came across that service when reading a WordPress blog.<\/p>\n<p>This is how it works: after uploading an image and creating your public profile on Gravatar, next time you log in to a Gravatar-enabled site or platform, that image and profile will automatically follow you there through Gravatar\u2019s association of your email address with a hash and an avatar.<\/p>\n<p>This post describes how to build a replica of Gravatar with more image-oriented features. You\u2019ll likely find it eye opening and fun.<\/p>\n<h2>Gravatar Features<\/h2>\n<p>Gravatar creates URLs according to the hashed value of email addresses, after which user avatars become accessible in the syntax <code>https:\/\/www.gravatar.com\/avatar\/&lt;hash&gt;<\/code>, e.g.:<\/p>\n<p><code>https:\/\/www.gravatar.com\/avatar\/205e460b479e2e5b48aec07710c08d50<\/code><\/p>\n<p>Here\u2019s one wrapped in an <code>&lt;img&gt;<\/code> tag:\n<img decoding=\"async\" src=\"https:\/\/www.gravatar.com\/avatar\/205e460b479e2e5b48aec07710c08d50\" \/><\/p>\n<p>You can specify the size for the image by adding the <code>s<\/code> parameter, e.g.:<\/p>\n<p><code>https:\/\/www.gravatar.com\/avatar\/205e460b479e2e5b48aec07710c08d50?s=200<\/code><\/p>\n<p>Absent the <code>s<\/code> parameter in the URL, Gravatar returns an 80&#215;80 image by default.<\/p>\n<p>You as developers can do the following with Gravatar:<\/p>\n<ul>\n<li>Display a default image if the hash is invalid or if the user has no avatar attached to his or her email address.<\/li>\n<li>Display  a <code>404<\/code> error message if no image is attached to the hash.<\/li>\n<li>Display a simple, cartoon-style silhouette by adding <code>d=mp<\/code> to the URL.<\/li>\n<li>Return a generated robot with various colors and faces by adding <code>_d=robohash<\/code> to the URL.<\/li>\n<li>Combine query parameters.<\/li>\n<li>Add the <code>r=<\/code> or <code>rating=<\/code> parameter to the URL so that your users can denote whether their images are appropriate for certain audiences. Below are your value choices. Specify one to request images up to and including that rating.\n<ul>\n<li>\n<code>g:<\/code> Contains no offensive content, hence suitable for all audiences.<\/li>\n<li>\n<code>pg:<\/code> Might contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence.<\/li>\n<li>\n<code>r:<\/code> Might contain harsh profanity, intense violence, nudity, or hard-drug use.<\/li>\n<li>\n<code>x:<\/code> Might contain hard-core sexual imagery or extremely disturbing violence.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Clavatar: Your Own Enhanced Gravatar Service<\/h2>\n<p>Let\u2019s call the enhanced Gravator service Clavatar, with which you can do the following:<\/p>\n<ul>\n<li>Return a 100&#215;100 image by default if the hash is invalid or if the user has no avatar attached to his or her email address.<\/li>\n<li>Request for one of the following versions of an image by adding the related parameter to the URL:\n<ul>\n<li>Any size with the size (<code>s<\/code>) parameter, e.g., <code>s=400<\/code> or <code>size=400<\/code>.<\/li>\n<li>A cartoonized version with <code>d=cp<\/code>.<\/li>\n<li>A black-and white version with <code>d=bw<\/code>.<\/li>\n<li>An artistic version with <code>d=hk<\/code>.<\/li>\n<li>A compressed and optimized version with <code>q=auto<\/code>.<\/li>\n<li>The best format with <code>f=auto<\/code>. This version ensures the most suitable format for whichever browser is delivering the image.<\/li>\n<li>A rounded-corners version (like the thumbnail on all platforms) with <code>rc=y<\/code>.<\/li>\n<li>A color-bordered version with <code>b=&lt;color&gt;<\/code>, e.g., <code>b=red<\/code>.<\/li>\n<\/ul>\n<\/li>\n<li>Rotate the image with <code>r=&lt;angle&gt;<\/code>, e.g., <code>r=40<\/code>. The angle\u2019s value must be between 1 and 100.<\/li>\n<\/ul>\n<h2>Development Process<\/h2>\n<p>The development process for Clavatar comprises three major steps.<\/p>\n<h3>Set Up a Lumen Project<\/h3>\n<ol>\n<li>\n<p>Install <a href=\"https:\/\/getcomposer.org\/\">Composer<\/a> and <a href=\"https:\/\/www.php.net\/manual\/en\/install.php\">PHP<\/a> on your development or production machine and then run this command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">composer create-project --prefer-dist laravel\/lumen clavatar\n<\/code><\/span><\/pre>\n<\/li>\n<li>\n<p>Go to the <code>clavatar<\/code> directory and rename the <code>env.example<\/code> file to <code>.env<\/code>.<\/p>\n<\/li>\n<li>\n<p>Run the project with the command  <code>php -S localhost:8000 -t public<\/code>.<\/p>\n<\/li>\n<\/ol>\n<p>Your Lumen project is now up and running.<\/p>\n<h3>Set Up the Mechanics for Database Access, Facades, and User Registration<\/h3>\n<p><strong>1.<\/strong> Create a database called <code>clavatar<\/code> in your local MySQL server.\n<strong>2.<\/strong> Uncomment these two lines in the <code>bootstrap\/app.php<\/code> file to make available the Laravel Eloquent Object Relation Mapping (ORM) and Laravel facades for building the Clavatar capabilities:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ $app-&gt;withEloquent();<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><strong>3.<\/strong> Create a user migration file with this command line:<\/p>\n<pre><code>```bash\nphp artisan make:migration create_users_table\n```\n<\/code><\/pre>\n<p><strong>4.<\/strong> Add the following code to the file:<\/p>\n<pre><code>```php\n&lt;?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nclass CreateUsersTable extends Migration\n{\n    \/**\n    * Run the migrations.\n    *\n    * @return void\n    *\/\n    public function up()\n    {\n        Schema::create('users', function (Blueprint $table) {\n            $table-&gt;id();\n            $table-&gt;string('name');\n            $table-&gt;string('email')-&gt;unique();\n            $table-&gt;string('password');\n            $table-&gt;string('hash')-&gt;nullable();\n            $table-&gt;string('avatar_id')-&gt;nullable();\n            $table-&gt;timestamps();\n        });\n    }\n\n    \/**\n    * Reverse the migrations.\n    *\n    * @return void\n    *\/\n    public function down()\n    {\n        Schema::dropIfExists('users');\n    }\n}\n```\n<\/code><\/pre>\n<p><strong>5.<\/strong> Run <code>php artisan migrate<\/code> to run the migration.<\/p>\n<p><strong>6.<\/strong> Create a user controller with this command from the console:<\/p>\n<pre><code>```bash\nphp artisan make:controller UserController\n```\n<\/code><\/pre>\n<p><strong>7.<\/strong> Add the following method for creating users to the <code>UserController.php<\/code> file:<\/p>\n<pre><code>```php\n&lt;?php\n\nnamespace App\\Http\\Controllers;\n\nuse App\\Models\\User;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Hash;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Carbon\\Carbon;\n\nclass UserController extends Controller\n{\npublic function createUser(Request $request)\n    {\n        $name     = $request-&gt;get('name');\n        $email      = $request-&gt;get('email');\n        $password = Hash::make($request-&gt;get('password'));\n\n        $credentials = $request-&gt;only('email');\n\n        $user = User::where('email', $email)-&gt;exists();\n\n        if($user) {\n            return response()-&gt;json([\n                'status' =&gt; false,\n                'message' =&gt; 'User already exists. Please try with another email.'\n            ], 401);\n        }\n\n        $user = new User;\n        $user-&gt;name      = $name;\n        $user-&gt;email     = $email;\n        $user-&gt;password  = $password;\n        $user-&gt;hash      = md5(strtolower(trim($email)));\n        $user-&gt;save();\n\n        return response()-&gt;json([\n                'status' =&gt; true,\n                'message' =&gt; 'User created successfully'\n            ], 201);\n    }\n}\n```\n<\/code><\/pre>\n<p>The code above signs up users with their names, email addresses, and passwords. For each user, the code hashes the related email address with the MD5 function, as Gravatar does, saving the hash as a unique user identifier.<\/p>\n<p><strong>8.<\/strong> Add the following endpoint to the <code>routes\/web.php<\/code> file:<\/p>\n<pre><code>```php\n\u2026\n$router-&gt;post('users', 'UserController@createUser');\n...\n```\n<\/code><\/pre>\n<p><strong>9.<\/strong> Add a user by invoking the API endpoint on Postman or Insomnia:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/Screen_Shot_2020-12-01_at_6.03.34_AM_cw4j7l.png\" alt=\"Clavatar\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"776\"\/><\/p>\n<h3>Associate Images With User Accounts<\/h3>\n<p>Next, build the feature that enables users to attach an image to their Clavatar accounts.<\/p>\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Completing this project also requires the following tasks, usually through a UI, which are beyond the scope of this post:<\/p>\n<ol>\n<li>Build a user-login process.<\/li>\n<li>Obtain a bearer token.<\/li>\n<li>Enable image uploads by users after authentication by middleware that verifies the bearer token.<\/li>\n<\/ol><\/div>\n<p>To enable image uploads by users, do the following:<\/p>\n<p><strong>1.<\/strong> Install the <a href=\"https:\/\/github.com\/cloudinary\/cloudinary_php\/tree\/2.0.0-beta\">Cloudinary PHP Library, version 2.0.0-Beta<\/a> from your console:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">composer <span class=\"hljs-built_in\">require<\/span> <span class=\"hljs-string\">\"cloudinary\/cloudinary_php:&gt;2.0.0-beta\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>With that library, you can perform these tasks on the back end:<\/p>\n<ul>\n<li>Upload images synchronously and asynchronously.<\/li>\n<li>Compress and optimize images through transformations.<\/li>\n<li>Store and back-up images and other media files.<\/li>\n<li>Deliver media files instantly.<\/li>\n<\/ul>\n<p><strong>2.<\/strong> Go to your <a href=\"https:\/\/cloudinary.com\/users\/login\">Cloudinary dashboard<\/a> and, under <strong>Account Details<\/strong>, copy the value of the <strong>API environment variable<\/strong>, which looks like this:<\/p>\n<p><code>CLOUDINARY_URL=cloudinary:\/\/xxxxxxx:xxxxxxxx@unicodeveloper<\/code><\/p>\n<p>Paste it in the <code>.env<\/code> file of your project.<\/p>\n<p><strong>3.<\/strong> In your console, create an <code>AvatarController.php<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">php<\/span> <span class=\"hljs-selector-tag\">artisan<\/span> <span class=\"hljs-selector-tag\">make<\/span><span class=\"hljs-selector-pseudo\">:controller<\/span> <span class=\"hljs-selector-tag\">AvatarController<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Add the following <code>uploadImage<\/code> function to that file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Cloudinary<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">Resize<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">Effect<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">Format<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">Quality<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">RoundCorners<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">ArtisticFilter<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">Border<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Cloudinary<\/span>\\<span class=\"hljs-title\">Transformation<\/span>\\<span class=\"hljs-title\">Argument<\/span>\\<span class=\"hljs-title\">Color<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AvatarController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $cloudinary;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__construct<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;cloudinary = <span class=\"hljs-keyword\">new<\/span> Cloudinary(env(<span class=\"hljs-string\">'CLOUDINARY_URL'<\/span>));\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">uploadImage<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $image  = $request-&gt;file(<span class=\"hljs-string\">'image'<\/span>);\n        $userId = $request-&gt;get(<span class=\"hljs-string\">'user_id'<\/span>);\n\n        $uploadedImage = <span class=\"hljs-keyword\">$this<\/span>-&gt;cloudinary-&gt;uploadApi()-&gt;upload($image-&gt;getRealPath());\n\n        <span class=\"hljs-keyword\">if<\/span>($uploadedImage) {\n\n            User::where(<span class=\"hljs-string\">'id'<\/span>, $userId)-&gt;update(&#91;<span class=\"hljs-string\">'avatar_id'<\/span> =&gt; $uploadedImage&#91;<span class=\"hljs-string\">'public_id'<\/span>]]);\n\n            <span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;\n                <span class=\"hljs-string\">'status'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n                <span class=\"hljs-string\">'message'<\/span> =&gt; <span class=\"hljs-string\">'Upload successful'<\/span>\n            ], <span class=\"hljs-number\">200<\/span>);\n        }\n\n        <span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;\n            <span class=\"hljs-string\">'status'<\/span> =&gt; <span class=\"hljs-keyword\">false<\/span>,\n            <span class=\"hljs-string\">'message'<\/span> =&gt; <span class=\"hljs-string\">'Upload failed. Please try again'<\/span>\n        ], <span class=\"hljs-number\">500<\/span>);\n    }\n}\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The above code did the following:<\/p>\n<p><strong>1.<\/strong> Imported the Cloudinary class.<\/p>\n<p><strong>2.<\/strong> Instantiated the Cloudinary class in the constructor, creating an object that connects with your Cloudinary account.<\/p>\n<p><strong>3.<\/strong> Added a function that accepts the user ID (<code>user_id<\/code>) and an image (<code>image<\/code>), uploads the image to Cloudinary, and updates your database with the image name returned from Cloudinary.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">$uploadedImage = <span class=\"hljs-keyword\">$this<\/span>-&gt;cloudinary-&gt;uploadApi()-&gt;upload($image-&gt;getRealPath()); <span class=\"hljs-comment\">\/\/ Uploads image to Cloudinary<\/span>\n\nUser::where(<span class=\"hljs-string\">'id'<\/span>, $userId)-&gt;update(&#91;<span class=\"hljs-string\">'avatar_id'<\/span> =&gt; $uploadedImage&#91;<span class=\"hljs-string\">'public_id'<\/span>]]); <span class=\"hljs-comment\">\/\/ Updates your DB with the name of the image, which is the public ID (`public_id`).<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here\u2019s an example of Cloudinary\u2019s response after an upload:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-keyword\">Array<\/span>\n(\n  &#91;public_id] =&gt; c87hg9xfxrd4itiim3t0\n  &#91;version] =&gt; <span class=\"hljs-number\">1571218607<\/span>\n  &#91;signature] =&gt; f8645b000be7d717599affc89a068157e4748276\n  &#91;width] =&gt; <span class=\"hljs-number\">864<\/span>\n  &#91;height] =&gt; <span class=\"hljs-number\">576<\/span>\n  &#91;format] =&gt; jpg\n  &#91;resource_type] =&gt; image\n  &#91;created_at] =&gt; <span class=\"hljs-number\">2017<\/span><span class=\"hljs-number\">-06<\/span><span class=\"hljs-number\">-23<\/span>T13:<span class=\"hljs-number\">59<\/span>:<span class=\"hljs-number\">18<\/span>Z\n  &#91;bytes] =&gt; <span class=\"hljs-number\">120253<\/span>\n  &#91;type] =&gt; upload\n  &#91;url] =&gt; http:<span class=\"hljs-comment\">\/\/res.cloudinary.com\/demo\/image\/upload\/v1571218607\/c87hg9xfxrd4itiim3t0.jpg<\/span>\n  &#91;secure_url] =&gt; https:<span class=\"hljs-comment\">\/\/res.cloudinary.com\/demo\/image\/upload\/v1571218607\/c87hg9xfxrd4itiim3t0.jpg<\/span>\n)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><strong>4.<\/strong> Add the Upload API endpoint to <code>routes\/web.php<\/code>:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">   $router-&gt;post(<span class=\"hljs-string\">'upload'<\/span>, <span class=\"hljs-string\">'AvatarController@uploadImage'<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>As a test, upload an image (for example, the one specified by the URL below) with Postman or Insomnia:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/Screen_Shot_2020-12-01_at_6.41.24_AM_nlctix.png\" alt=\"Clavitar successful\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"776\"\/><\/p>\n<p>Finally, verify the following:<\/p>\n<ul>\n<li>The image you just uploaded is in your Cloudinary account\u2019s Media Library.<\/li>\n<li>The <code>avatar_id<\/code> value in your database is the same as the image\u2019s <code>public_id<\/code> value.<\/li>\n<\/ul>\n<p>You\u2019ve now associated an image with a user.<\/p>\n<p><a href=\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_2\">Part 2<\/a> describes how to make Clavatar work like Gravatar and to develop Clavatar\u2019s capabilities of enabling requests for various versions of the images related to user accounts. Stay tuned.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":22258,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[25,229,263],"class_list":["post-22257","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-asset-management","tag-php","tag-sdk"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How to Build an Enhanced Gravatar Service, Part 1<\/title>\n<meta name=\"description\" content=\"With the Cloudinary PHP Library, build an enhanced Gravatar service with options for various image versions associated with user accounts.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build an Enhanced Gravatar Service, Part 1\" \/>\n<meta property=\"og:description\" content=\"With the Cloudinary PHP Library, build an enhanced Gravatar service with options for various image versions associated with user accounts.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-01-04T17:17:18+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-05-27T21:13:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1540\" \/>\n\t<meta property=\"og:image:height\" content=\"847\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How to Build an Enhanced Gravatar Service, Part 1\",\"datePublished\":\"2021-01-04T17:17:18+00:00\",\"dateModified\":\"2024-05-27T21:13:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\"},\"wordCount\":8,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA\",\"keywords\":[\"Asset Management\",\"PHP\",\"SDK\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2021\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\",\"url\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\",\"name\":\"How to Build an Enhanced Gravatar Service, Part 1\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA\",\"datePublished\":\"2021-01-04T17:17:18+00:00\",\"dateModified\":\"2024-05-27T21:13:07+00:00\",\"description\":\"With the Cloudinary PHP Library, build an enhanced Gravatar service with options for various image versions associated with user accounts.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA\",\"width\":1540,\"height\":847},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Build an Enhanced Gravatar Service, Part 1\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to Build an Enhanced Gravatar Service, Part 1","description":"With the Cloudinary PHP Library, build an enhanced Gravatar service with options for various image versions associated with user accounts.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1","og_locale":"en_US","og_type":"article","og_title":"How to Build an Enhanced Gravatar Service, Part 1","og_description":"With the Cloudinary PHP Library, build an enhanced Gravatar service with options for various image versions associated with user accounts.","og_url":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1","og_site_name":"Cloudinary Blog","article_published_time":"2021-01-04T17:17:18+00:00","article_modified_time":"2024-05-27T21:13:07+00:00","og_image":[{"width":1540,"height":847,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e-jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1"},"author":{"name":"","@id":""},"headline":"How to Build an Enhanced Gravatar Service, Part 1","datePublished":"2021-01-04T17:17:18+00:00","dateModified":"2024-05-27T21:13:07+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1"},"wordCount":8,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA","keywords":["Asset Management","PHP","SDK"],"inLanguage":"en-US","copyrightYear":"2021","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1","url":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1","name":"How to Build an Enhanced Gravatar Service, Part 1","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA","datePublished":"2021-01-04T17:17:18+00:00","dateModified":"2024-05-27T21:13:07+00:00","description":"With the Cloudinary PHP Library, build an enhanced Gravatar service with options for various image versions associated with user accounts.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA","width":1540,"height":847},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_an_enhanced_gravatar_service_part_1#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Build an Enhanced Gravatar Service, Part 1"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649719658\/Web_Assets\/blog\/Cld_Blog_Img_Gravitars_1_222584c66e\/Cld_Blog_Img_Gravitars_1_222584c66e.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/22257","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=22257"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/22257\/revisions"}],"predecessor-version":[{"id":33944,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/22257\/revisions\/33944"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/22258"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=22257"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=22257"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=22257"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}