https://i.ytimg.com/vi/nLldkBTp9Js/maxresdefault.jpg
AboutPressCopyrightContact usCreatorsAdvertiseDevelopersTermsPrivacyPolicy & SafetyHow YouTube worksTest new featuresNFL Sunday Ticket
Laravel News Links
Just another WordPress site
https://i.ytimg.com/vi/nLldkBTp9Js/maxresdefault.jpg
AboutPressCopyrightContact usCreatorsAdvertiseDevelopersTermsPrivacyPolicy & SafetyHow YouTube worksTest new featuresNFL Sunday Ticket
Laravel News Links
https://media.townhall.com/townhall/reu/o/2023/61/699f5cc2-b3ae-4733-9939-d4a7966fdb29-860×475.png
https://cdn0.thetruthaboutguns.com/wp-content/uploads/2023/11/shutterstock_2555897-scaled.jpg
By Tyler Hilliker
You have probably heard of the world famous Uzi and its variants, even if you aren’t really a “gun person.” The infamous little 9mm is just one of those guns that’s almost universally recognizable by almost anyone, much like the Tommy gun, M16, AK47 and GLOCK.
The Uzi was first introduced in Israel in the early 1950s, seeing use through the present day with several militaries, including the ongoing Russia-Ukraine war. Uzis of one kind or another have also been featured in literally hundreds of action films, TV shows, music videos and video games. If you’ve ever seen photographs from the 1981 attempted assassination of Ronald Reagan then you have surely seen the little sub gun being famously wielded by Secret Service agents.
Today we will be taking a look at the life of its inventor. I present to you the man, the myth, the legend…Uziel Gal.
This year marks the 100th anniversary of the birth of the Uzi, born as Gotthard Glas. Born on December 15th 1923 in Weimar Germany to a Jewish family, they to England in 1933 after the Nazis came into power. Then in 1936, they moved to Kibbutz Yagur in what was then the British Mandate of Palestine at which time he also changed his name to Uziel Gal.
From his youth, Gal was interested in weapons engineering. He designed his first automatic gun (which shot arrows) at age 15. At 20, now a member of the Haganah defense force and was arrested by British troops for carrying a gun (it was forbidden to Jews in Israel at the time). He spent three years in prison during which he studied mechanical engineering before being pardoned in 1946.
He joined what is now known as the Israeli Defense Force during the Israeli war of Independence in 1948 at the age of 24 where he saw combat in Northern Israel. That’s where he demonstrated his homemade prototype submachine gun in Yagur. It was during that time that Captain (later Major) Gal was sent to work at Israel Military Industries where his Uzi first went into production in 1950. It was adopted officially in 1951 and first saw use with IDF special forces in 1954. Eventually over 10 million would be made.
Interestingly enough, Uziel didn’t even want the gun named after himself, but it proved to be so popular that his requests were ignored. In 1955, the IDF decorated him for his work with the Tzalash HaRamatkal. And in 1958, he became the first recipient of the Israel Security Award which was given to him by Israeli Prime Minister David Ben-Gurion.
Uziel remained with the IDF until 1975, rising to the rank of Lt. Col. when he retired. Shortly after, in 1976, he and his family emigrated to the United States, specifically Philadelphia, so that his daughter, Tamar, could receive proper extended medical care for a rare brain disorder. She unfortunately passed away in 1984 in her early 20s.
After coming to America, Uziel worked with Action Manufacturing (Action Arms) originally prototyping a .30 caliber military rifle. In 1978 He was approached by the owner of Action Arms to bring his Uzi to the U.S. civilian market.
As a career IDF officer, Uziel had always felt that his invention for the Jewish state was part of his duty, and he never received any royalties on the original military design.
Working with Israel Military Industries, the first intended civilian model was sent to the ATF for approval, but was quickly denied, considered readily convertible to a machinegun. As it was simply a 16” barreled Uzi sub-machinegun with a metal bar welded into the removable grip frame to prevent the selector from being switched to full auto, it’s quite obvious why this original model was denied for import as a semi-auto.
Uziel then worked to design a truly semi-automatic only version of his design, which included a welded steel bar inside the receiver to prevent installation of a full auto bolt, and a change to a closed bolt design, amongst many smaller changes.
With an Uzi model newly approved for import, the gun was first introduced to the public at the 1980 SHOT Show, receiving a significantly higher than expected 7,000 orders initially. In the first three years, they managed to sell more than 36,000 additional semi auto Uzi’s.
In 1989, an American assault weapons import ban went into effect. Essentially dooming the Action Arms’ plans. The Uzi is still being manufactured in both licensed and unlicensed through the present day.
Uziel eventually left Action Arms over a royalty dispute. He sued the company and won after four years of litigation. In the end, Uziel was awarded a substantial sum for royalties, which had been bumped from 5% to 10% by the judge, as well as a large sum in damages.
In his later years, Uziel worked with Ruger on the development of the Ruger MP9 Sub Machinegun, as well as a number of of other small projects. His wife passed away in 1998, and Uziel died in September, 2002. His body was returned to Israel where he is buried alongside his wife at the foot of Mount Carmel.
Uziel Gal will be remembered for his distinguished service to Israel, for his extraordinary creativity and the iconic, eponymous submachinegun he invented. He is survived by his son, Iddo Gal.
Tyler Hilliker is a USCCA certified firearms instructor, 2A advocate, and federally licensed firearms manufacturer. He is a vocal advocate, fostering informed discussions and is working to leave an indelible mark on the firearms community through education, training, and unwavering advocacy.
The Truth About Guns
https://hips.hearstapps.com/amv-prod-gp.s3.amazonaws.com/gearpatrol/wp-content/uploads/2016/11/Carve-A-Turkey-Gear-Patrol-Lead-Featured.jpg?crop=1xw:0.65xh;center,top&resize=640:*
The first mistake you can make when carving a turkey is to attempt the whole ordeal table-side. Bad idea. “Most people don’t have a giant cutting board that they’re going to put on their table,” says Harry Rosenblum, cofounder of The Brooklyn Kitchen. “They have a platter, and that’s a horrible surface to cut on. The thing slides around. You don’t have control. Also, nobody wants to see you stick your hands on the turkey.”
Even in the comfort of the kitchen, however, carving a bird can induce varying levels of performance anxiety among turkey novices — especially if that’s where the crowd has gathered. Just remember now: you’re feeding friends and family, not competing on Chopped.
“Ultimately, as long as the turkey is cooked correctly, it’s not going to taste bad,” Rosenblum says. “There’s very little about carving turkey that could cause you to screw up Thanksgiving. If you got here, you’re basically all set.” So this year, get in the driver’s seat and carve that goddamn turkey. Just don’t do it at the table. Real Thanksgivings don’t need to look like Norman Rockwell paintings.
Like with steak or pork, let your roasted turkey rest at room temperature for 15 to 30 minutes before carving. Use that time to check your potatoes, make gravy, drink more wine — but most of all, to make sure your chef’s knife is sharp and honed. That’s crucial if you want skin on every piece, Rosenblum says.
“The end goal is to take everything off the carcass,” Rosenblum says. “I like to start with the leg and thigh, following the natural seams of the animal.” To completely sever the thigh from the breast, you’ll need to cut through a knuckle on either side.
Flip the leg and thigh skin-side-down so you can locate the knuckle. Cut straight through it with your knife. “If the bird is fully cooked, the joint will just pop out,” Rosenblum says.
Locate the thigh bone and use the tip of your knife to cut around it. “There will be a little bit of meat on the bone,” Rosenblum says. “Save them. They’re great for soup.”
Flip the boneless thigh back skin-side-up, and slice into individual half-inch pieces. “Always slice against the grain of the muscle,” Rosenblum says.
“The body comes up into a point,” says Rosenblum. That’s the breastbone. “Once you cut down either side, you’ll see the meat will start to come away from the carcass.” Cut through the ball joint that connects the breast and wing to the carcass.
Before you slice the breast meat, remove the wing by locating the connective ball joint and removing it from the breast. You want a boneless piece of breast meat.
Follow the same rule of thumb applied to the thigh: with the skin-side-up, and going against the grain, slice the breast meat into half-inch pieces.
“You can go as rustic or refined as you want,” Rosenblum says. “I always try to put the turkey back together on the plate, just without the bones.” This makes it easy for people to identify and choose the kind of meat they want.
Gear Patrol
https://ahtesham.me/storage/posts/September2023/Af1588tH7AwEovS2wV66.jpg
In this tutorial, we will learn how to find the nearest location based on latitude and longitude using Laravel 10 in a simple and easy-to-follow manner. I’ll guide you through the steps to make use of Laravel 10’s latitude and longitude tools to determine the closest location. Specifically, we’ll focus on finding nearby locations in Laravel 10. Follow these straightforward steps to utilize Laravel 10 for locating the nearest place based on latitude and longitude.
Having access to latitude and longitude coordinates can be highly valuable for various purposes, such as identifying nearby businesses, residences, hotels, cities, and more. In this example, we’ll take a user-friendly approach. To enable location-based user searches by their closest latitude and longitude, we’ll add latitude and longitude fields to the users’ table.
Let’s get started with these easy-to-follow instructions:
first of all we need to get fresh Laravel version application using bellow command, So open your terminal OR command prompt and run bellow command
composer create-project laravel/laravel blog
In this is step we need to create one route for getting near location from lat and long example.
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\LocationController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('near-by-places', [LocationController::class, 'index']);
Read More : Laravel 10 Multiple Authentications using Middleware Example
in this step, we need to create LocationController and add following code on that file. you have users table with lat and long columns. also add some dummy records on table as well.
app/Http/Controllers/LocationController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Model\User;
class LocationController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$lat = YOUR_CURRENT_LATTITUDE;
$lon = YOUR_CURRENT_LONGITUDE;
$users = User::select("users.id"
,DB::raw("6371 * acos(cos(radians(" . $lat . "))
* cos(radians(users.lat))
* cos(radians(users.lon) - radians(" . $lon . "))
+ sin(radians(" .$lat. "))
* sin(radians(users.lat))) AS distance"))
->groupBy("users.id")
->get();
dd($users);
}
}
All the required steps have been done, now you have to type the given below command and hit enter to run the Laravel app:
php artisan serve
Now, Go to your web browser, type the given URL and view the app output:
http://localhost:8000/near-by-places
i hope it can help you…
Laravel News Links
https://ahtesham.me/storage/posts/September2023/BTEscC4HKqWPfEpZoOji.jpg
In this tutorial, we’ll learn how to create a RESTful API using Laravel 10 and Laravel Sanctum. We’ll also explore how to test CRUD operations (create, read, update, delete) on RESTful APIs with Sanctum authentication in Laravel 10. This is a simple example of working with Laravel 10 Sanctum.
Throughout this tutorial, you’ll discover how to build APIs in Laravel with the help of the Laravel Sanctum package. Sanctum authenticates incoming HTTP requests by checking the Authorization header, which contains a valid API token. It efficiently manages user API tokens by storing them in a single database table.
Let us begin the tutorial by installing a new laravel application. if you have already created the project, then skip following step.
composer create-project laravel/laravel example-app
In this step we need to install sanctum via the Composer package manager, so one your terminal and fire bellow command:
composer require laravel/sanctum
After successfully install package, we need to publish configuration file with following command:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
we require to get default migration for create new sanctum tables in our database. so let’s run bellow command.
php artisan migrate
Next, we need to add middleware for sanctum api, so let’s add as like bellow:
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
In this step, we have to configuration on three place model, service provider and auth config file. So you have to just following change on that file.
In model we added HasApiTokens class of Sanctum,
In auth.php, we added api auth configuration.
app/Models/User.php
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Read More : Laravel 10 Get Current Logged in User Data Example
next, we require to create migration for posts table using Laravel 10 php artisan command, so first fire bellow command:
php artisan make:migration create_posts_table
After this command you will find one file in following path database/migrations and you have to put bellow code in your migration file for create posts table.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
};
After create migration we need to run above migration by following command:
php artisan migrate
After create “posts” table you should create Post model for posts, so first create file in this path app/Models/Post.php and put bellow content in item.php file:
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'title', 'body'
];
}
In this step, we will create api routes for login, register and posts rest api. So, let’s add new route on that file.
routes/api.php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\RegisterController;
use App\Http\Controllers\API\PostController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::controller(RegisterController::class)->group(function(){
Route::post('register', 'register');
Route::post('login', 'login');
});
Route::middleware('auth:sanctum')->group( function () {
Route::resource('posts', PostController::class);
});
For the next step, we’ll create three new controllers: BaseController, PostController, and RegisterController. To keep our API controllers organized, we’ll create a new folder named “API” within the Controllers directory. Here’s how to create these controllers:
php artisan make:controller BaseController
app/Http/Controllers/API/BaseController.php
<?php
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller as Controller;
class BaseController extends Controller
{
/**
* success response method.
*
* @return \Illuminate\Http\Response
*/
public function sendResponse($result, $message)
{
$response = [
'success' => true,
'data' => $result,
'message' => $message,
];
return response()->json($response, 200);
}
/**
* return error response.
*
* @return \Illuminate\Http\Response
*/
public function sendError($error, $errorMessages = [], $code = 404)
{
$response = [
'success' => false,
'message' => $error,
];
if(!empty($errorMessages)){
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
}
php artisan make:controller RegisterController
app/Http/Controllers/API/RegisterController.php
<?php
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseController as BaseController;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Validator;
class RegisterController extends BaseController
{
/**
* Register api
*
* @return \Illuminate\Http\Response
*/
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'password' => 'required',
]);
if($validator->fails()){
return $this->sendError('Validation Error.', $validator->errors());
}
$input = $request->all();
$input['password'] = bcrypt($input['password']);
$user = User::create($input);
$success['token'] = $user->createToken('MyApp')->plainTextToken;
$success['name'] = $user->name;
return $this->sendResponse($success, 'User register successfully.');
}
/**
* Login api
*
* @return \Illuminate\Http\Response
*/
public function login(Request $request)
{
if(Auth::attempt(['email' => $request->email, 'password' => $request->password])){
$user = Auth::user();
$success['token'] = $user->createToken('MyApp')->plainTextToken;
$success['name'] = $user->name;
return $this->sendResponse($success, 'User login successfully.');
}
else{
return $this->sendError('Unauthorised.', ['error'=>'Unauthorised']);
}
}
}
php artisan make:controller PostController
app/Http/Controllers/API/PostController.php
<?php
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseController as BaseController;
use App\Http\Resources\PostResource;
use App\Models\Post;
use Validator;
class PostController extends BaseController
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$posts = Post::all();
return $this->sendResponse(PostResource::collection($posts), 'Post retrieved successfully.');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$input = $request->all();
$validator = Validator::make($input, [
'title' => 'required',
'body' => 'required'
]);
if($validator->fails()){
return $this->sendError('Validation Error.', $validator->errors());
}
$post = Post::create($input);
return $this->sendResponse(new PostResource($post), 'Post created successfully.');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$post = Post::find($id);
if (is_null($post)) {
return $this->sendError('Post not found.');
}
return $this->sendResponse(new PostResource($post), 'Post retrieved successfully.');
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Post $post)
{
$input = $request->all();
$validator = Validator::make($input, [
'title' => 'required',
'body' => 'required'
]);
if($validator->fails()){
return $this->sendError('Validation Error.', $validator->errors());
}
$post->title = $input['title'];
$post->body = $input['body'];
$post->save();
return $this->sendResponse(new PostResource($post), 'Post updated successfully.');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Post $post)
{
$post->delete();
return $this->sendResponse([], 'Post deleted successfully.');
}
}
Creating API resources in Laravel is indeed an important step for building a REST API. API resources help you define a consistent and customizable response format for your model objects. To create an API resource in Laravel 10, you can use the following command:
php artisan make:resource PostResource
Now there created new file with new folder on following path:
app/Http/Resources/PostResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'created_at' => $this->created_at->format('d/m/Y'),
'updated_at' => $this->updated_at->format('d/m/Y'),
];
}
}
All steps have been done, now you have to type the given command and hit enter to run the laravel app:
php artisan serve
Now, you have to open web browser, type the given URL and view the app output:
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
]
Here is Routes URL with Verb:
Now simply you can run above listed URL like as bellow screen shot:
1. Register API: Verb:GET, URL:http://localhost:8000/api/register
2. Login API: Verb:GET, URL:http://localhost:8000/api/login
3. Post List API: Verb:GET, URL:http://localhost:8000/api/posts
4. Post Create API: Verb:GET, URL:http://localhost:8000/api/posts
5. Post Show API: Verb:GET, URL:http://localhost:8000/api/posts/{id}
6. Post Update API: Verb:PUT, URL:http://localhost:8000/api/posts/{id}
7. Post Delete API: Verb:DELETE, URL:http://localhost:8000/api/posts/{id}
I hope it can help you…
Laravel News Links
https://GunFreeZone.net/wp-content/uploads/2016/01/cropped-gfz-ico.png
This killed me…
OMG pic.twitter.com/MzftUclptQ
— General (@TheGeneral_0) November 15, 2023
Gun Free Zone
https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/headlamps-and-lanterns-color-mode-lead-6555141dcff52.jpg?crop=1.00xw:0.752xh;0,0.0938xh&resize=640:*
Have you ever wondered why headlamps and camp lights come with a red or orange setting? It’s not just for ambience — there’s a surprising benefit to switching from bright white to more mellow red. If you’re sick of dealing with a tent full of flying bugs, this info is for you.
According to the North Carolina State University Agriculture and State Sciences Department: “Most insects have only two types of visual pigments. One pigment absorbs green and yellow light (550 nm); the other absorbs blue and ultraviolet light (<480 nm). Insects cannot see red.” Even if the reasons don’t interest you, the results should. Your camping lantern has a red light mode. Use it, and you won’t attract insects.
I’ve tried this trick myself: At camp I typically use a Biolite Headlamp 325 and Goal Zero Crush Light Chroma, when I’m not testing other lighting solutions. They’re both lightweight, easy to operate and the Goal Zero has solar charging; I leave it on the dash to soak up the sun during the day’s drive, and it’s ready to go when then sun starts to set.
First, I tested the headlamp: Anyone who’s worn a headlamp during the evening at camp is probably familiar with the flickering, flying bugs that are drawn to your face almost immediately after you turn your headlamp on. It’s annoying and can make seeing clearly a chore, cancelling out the appeal of the headlamp in the first place. Sure enough, with the standard white light setting I had bugs up in my grill; once I switched it to red, they all left me alone.
Same with the Crush Light Chroma. We keep one in our GoFast Camper and have built a routine around the red light. When we’re getting into the tent and need to see and have panels open, we set the Chroma to red. No bugs follow us inside, and once we’re in and all the tent flaps are securely shut, we’ll switch to white light. It is trickier to see details at night with red light, so we mostly use if for entering and exiting the tent, and reserve the white light reserved for use after we’ve tucked in for the evening.
Nearly all headlamps come with a red light setting, but read the product description rather than assuming. Camp lights and lanterns aren’t equipped with red light as widely, so check to make sure your pick has the ability to go into red mode. With that in mind, here are a few of my favorites for keeping bugs at bay.
Gear Patrol
https://planetscale.com/assets/blog/content/mysql-replication-best-practices-and-considerations/mysql-replication-best-practices-social.jpgLearn the best practices for configuring MySQL replication, and how to ensure your data is always available.Planet MySQL
https://hashnode.com/utility/r?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1699585660555%2F4345ca45-14b3-45cd-ac5a-824e7fe8ee60.png%3Fw%3D1200%26h%3D630%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp%26fm%3Dpng
I’ve been working a lot lately integrating third-party APIs. There are several different approaches to this such as using the third-party provided SDK. However, I feel sticking to Laravel’s Http
facade is often a better choice. By using the Http
facade, all third-party integrations can have a similar structure, and testing and mocking becomes a lot easier. Also, your application will have fewer dependencies. You won’t have to worry about keeping the SDK up to date or figuring out what to do if the SDK is no longer supported.
In this post, we will explore integrating the Google Books API. I will create a reusable client and request class to make using the API very simple. In future posts, I will go into more detail about testing, mocking, as well as creating API resources.
Let’s get started!
Now that we have an API key, we can add it to the .env
along with the API URL.
GOOGLE_BOOKS_API_URL=https://www.googleapis.com/books/v1
GOOGLE_BOOKS_API_KEY=[API KEY FROM GOOGLE]
For this example, I am storing an API key that I obtained from the Google Cloud console, though it is not needed for the parts of the API we will be accessing. For more advanced API usage, you would need to integrate with Google’s OAuth 2.0 server and create a client ID and secret that could also be stored in the
.env
file. This is beyond the scope of this post.
With the environment variables in place, open the config/services.php
file and add a section for Google Books.
'google_books' => [
'base_url' => env('GOOGLE_BOOKS_API_URL'),
'api_key' => env('GOOGLE_BOOKS_API_KEY'),
],
When making requests to the API, I find it easiest to use a simple class to be able to set any request properties I need.
Below is an example of an ApiRequest
class that I use to pass in URL information along with the body, headers, and any query parameters. This class can easily be modified or extended to add additional functionality.
<?php
namespace App\Support;
class ApiRequest
{
protected array $headers = [];
protected array $query = [];
protected array $body = [];
public function __construct(protected HttpMethod $method = HttpMethod::GET, protected string $uri = '')
{
}
public function setHeaders(array|string $key, string $value = null): static
{
if (is_array($key)) {
$this->headers = $key;
} else {
$this->headers[$key] = $value;
}
return $this;
}
public function clearHeaders(string $key = null): static
{
if ($key) {
unset($this->headers[$key]);
} else {
$this->headers = [];
}
return $this;
}
public function setQuery(array|string $key, string $value = null): static
{
if (is_array($key)) {
$this->query = $key;
} else {
$this->query[$key] = $value;
}
return $this;
}
public function clearQuery(string $key = null): static
{
if ($key) {
unset($this->query[$key]);
} else {
$this->query = [];
}
return $this;
}
public function setBody(array|string $key, string $value = null): static
{
if (is_array($key)) {
$this->body = $key;
} else {
$this->body[$key] = $value;
}
return $this;
}
public function clearBody(string $key = null): static
{
if ($key) {
unset($this->body[$key]);
} else {
$this->body = [];
}
return $this;
}
public function getHeaders(): array
{
return $this->headers;
}
public function getQuery(): array
{
return $this->query;
}
public function getBody(): array
{
return $this->body;
}
public function getUri(): string
{
if (empty($this->query) || $this->method === HttpMethod::GET) {
return $this->uri;
}
return $this->uri.'?'.http_build_query($this->query);
}
public function getMethod(): HttpMethod
{
return $this->method;
}
public static function get(string $uri = ''): static
{
return new static(HttpMethod::GET, $uri);
}
public static function post(string $uri = ''): static
{
return new static(HttpMethod::POST, $uri);
}
public static function put(string $uri = ''): static
{
return new static(HttpMethod::PUT, $uri);
}
public static function delete(string $uri = ''): static
{
return new static(HttpMethod::DELETE, $uri);
}
}
The class constructor takes an HttpMethod
, which is just a simple enum with the various HTTP methods, and a URI.
enum HttpMethod: string
{
case GET = 'get';
case POST = 'post';
case PUT = 'put';
case DELETE = 'delete';
}
There are helper methods to create the request using the HTTP method name and passing a URI. Finally, there are methods to add and clear headers, query parameters, and body data.
Now that we have the request, we need an API client to send it. This is where we can use the Http
facade.
First, we’ll create an abstract ApiClient
class that will be extended by our various APIs.
<?php
namespace App\Support;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
abstract class ApiClient
{
public function send(ApiRequest $request): Response
{
return $this->getBaseRequest()
->withHeaders($request->getHeaders())
->{$request->getMethod()->value}(
$request->getUri(),
$request->getMethod() === HttpMethod::GET
? $request->getQuery()
: $request->getBody()
);
}
protected function getBaseRequest(): PendingRequest
{
$request = Http::acceptJson()
->contentType('application/json')
->throw()
->baseUrl($this->baseUrl());
return $this->authorize($request);
}
protected function authorize(PendingRequest $request): PendingRequest
{
return $request;
}
abstract protected function baseUrl(): string;
}
This class has a getBaseRequest
method that sets up some sane defaults using the Http
facade to create a PendingRequest
. It calls the authorize
method which we can override in our Google Books implementation to set our API key.
The baseUrl
method is just a simple abstract method that our Google Books class will set to use the Google Books API URL we set earlier.
Finally, the send
method is what sends the request to the API. It takes an ApiRequest
parameter to build up the request, then returns the response.
With the abstract client created, we can now create a GoogleBooksApiClient
to extend it.
<?php
namespace App\Support;
use Illuminate\Http\Client\PendingRequest;
class GoogleBooksApiClient extends ApiClient
{
protected function baseUrl(): string
{
return config('services.google_books.base_url');
}
protected function authorize(PendingRequest $request): PendingRequest
{
return $request->withQueryParameters([
'key' => config('services.google_books.api_key'),
]);
}
}
In this class, we just need to set the base URL and configure the authorization. For the Google Books API, that means passing the API key as a URL parameter and setting an empty Authorization
header.
If we had an API that used a bearer authorization, we could have an authorize
method like the following:
protected function authorize(PendingRequest $request): PendingRequest
{
return $request->withToken(config(services.someApi.token));
}
The nice part about having this authorize
method is the flexibility it offers to support a variety of API authorization methods.
Now that we have our ApiRequest
class and GoogleBooksApiClient
, we can create an action to query books by title. It would look something like this:
<?php
namespace App\Actions;
use App\Support\ApiRequest;
use App\Support\GoogleBooksApiClient;
use Illuminate\Http\Client\Response;
class QueryBooksByTitle
{
public function __invoke(string $title): Response
{
$client = app(GoogleBooksApiClient::class);
$request = ApiRequest::get('volumes')
->setQuery('q', 'intitle:'.$title)
->setQuery('printType', 'books');
return $client->send($request);
}
}
Then, to call the action, if I wanted to find information about the book The Ferryman, which I just read and highly recommend, use the following snippet:
use App\Actions\QueryBooksByTitle;
$response = app(QueryBooksByTitle::class)("The Ferryman");
$response->json();
Below, I added some examples for testing the request and client classes. For the tests, I am using Pest PHP which provides a clean syntax and additional features on top of PHPUnit.
<?php
use App\Support\ApiRequest;
use App\Support\HttpMethod;
it('sets request data properly', function () {
$request = (new ApiRequest(HttpMethod::GET, '/'))
->setHeaders(['foo' => 'bar'])
->setQuery(['baz' => 'qux'])
->setBody(['quux' => 'quuz']);
expect($request)
->getHeaders()->toBe(['foo' => 'bar'])
->getQuery()->toBe(['baz' => 'qux'])
->getBody()->toBe(['quux' => 'quuz'])
->getMethod()->toBe(HttpMethod::GET)
->getUri()->toBe('/');
});
it('sets request data properly with a key->value', function () {
$request = (new ApiRequest(HttpMethod::GET, '/'))
->setHeaders('foo', 'bar')
->setQuery('baz', 'qux')
->setBody('quux', 'quuz');
expect($request)
->getHeaders()->toBe(['foo' => 'bar'])
->getQuery()->toBe(['baz' => 'qux'])
->getBody()->toBe(['quux' => 'quuz'])
->getMethod()->toBe(HttpMethod::GET)
->getUri()->toBe('/');
});
it('clears request data properly', function () {
$request = (new ApiRequest(HttpMethod::GET, '/'))
->setHeaders(['foo' => 'bar'])
->setQuery(['baz' => 'qux'])
->setBody(['quux' => 'quuz']);
$request->clearHeaders()
->clearQuery()
->clearBody();
expect($request)
->getHeaders()->toBe([])
->getQuery()->toBe([])
->getBody()->toBe([])
->getUri()->toBe('/');
});
it('clears request data properly with a key', function () {
$request = (new ApiRequest(HttpMethod::GET, '/'))
->setHeaders('foo', 'bar')
->setQuery('baz', 'qux')
->setBody('quux', 'quuz');
$request->clearHeaders('foo')
->clearQuery('baz')
->clearBody('quux');
expect($request)
->getHeaders()->toBe([])
->getQuery()->toBe([])
->getBody()->toBe([])
->getUri()->toBe('/');
});
it('creates instance with correct method', function (HttpMethod $method) {
$request = ApiRequest::{$method->value}('/');
expect($request->getMethod())->toBe($method);
})->with([
[HttpMethod::GET],
[HttpMethod::POST],
[HttpMethod::PUT],
[HttpMethod::DELETE],
]);
The ApiRequest
tests check that the correct request data is being set and the correct methods are being used.
Testing for the ApiClient
will be a little more complex. Since it is an abstract class, we will use an anonymous class in the beforeEach
function to create a client to use that extends ApiClient
.
Notice, that we also use the Http::fake()
method. This creates mocks on the Http
facade that we can make assertions against and prevent making API requests in the tests.
<?php
use App\Support\ApiClient;
use App\Support\ApiRequest;
use App\Support\HttpMethod;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
beforeEach(function () {
Http::fake();
$this->client = new class extends ApiClient
{
protected function baseUrl(): string
{
return 'https://example.com';
}
};
});
it('sends a get request', function () {
$request = ApiRequest::get('foo')
->setHeaders(['X-Foo' => 'Bar'])
->setQuery(['baz' => 'qux']);
$this->client->send($request);
Http::assertSent(static function (Request $request) {
expect($request)
->url()->toBe('https://example.com/foo?baz=qux')
->method()->toBe(HttpMethod::GET->name)
->header('X-Foo')->toBe(['Bar']);
return true;
});
});
it('sends a post request', function () {
$request = ApiRequest::post('foo')
->setBody(['foo' => 'bar'])
->setHeaders(['X-Foo' => 'Bar'])
->setQuery(['baz' => 'qux']);
$this->client->send($request);
Http::assertSent(static function (Request $request) {
expect($request)
->url()->toBe('https://example.com/foo?baz=qux')
->method()->toBe(HttpMethod::POST->name)
->data()->toBe(['foo' => 'bar'])
->header('X-Foo')->toBe(['Bar']);
return true;
});
});
it('sends a put request', function () {
$request = ApiRequest::put('foo')
->setBody(['foo' => 'bar'])
->setHeaders(['X-Foo' => 'Bar'])
->setQuery(['baz' => 'qux']);
$this->client->send($request);
Http::assertSent(static function (Request $request) {
expect($request)
->url()->toBe('https://example.com/foo?baz=qux')
->method()->toBe(HttpMethod::PUT->name)
->data()->toBe(['foo' => 'bar'])
->header('X-Foo')->toBe(['Bar']);
return true;
});
});
it('sends a delete request', function () {
$request = ApiRequest::delete('foo')
->setBody(['foo' => 'bar'])
->setHeaders(['X-Foo' => 'Bar'])
->setQuery(['baz' => 'qux']);
$this->client->send($request);
Http::assertSent(static function (Request $request) {
expect($request)
->url()->toBe('https://example.com/foo?baz=qux')
->method()->toBe(HttpMethod::DELETE->name)
->data()->toBe(['foo' => 'bar'])
->header('X-Foo')->toBe(['Bar']);
return true;
});
});
it('handles authorization', function () {
$client = new class extends ApiClient
{
protected function baseUrl(): string
{
return 'https://example.com';
}
protected function authorize(PendingRequest $request): PendingRequest
{
return $request->withHeaders(['Authorization' => 'Bearer foo']);
}
};
$request = ApiRequest::get('foo');
$client->send($request);
Http::assertSent(static function (Request $request) {
expect($request)->header('Authorization')->toBe(['Bearer foo']);
return true;
});
});
For the tests, we are confirming that the request properties are being set correctly on the various request methods. We also confirm the baseUrl
and authorize
methods are being called correctly. To make these assertions, we are using the Http::assertSent
method which expects a callback with a $request
that we can test against. Notice that I am using the PestPHP expectations and then returning true
. We could just use a normal comparison and return that, but by using the expectations, we get much cleaner error messages when the tests fail. Read this excellent article for more information.
The test for the GoogleBooksApiClient
is similar to the ApiClient
test where we just want to make sure our custom implementation details are being handled properly, like setting the base URL and adding a query parameter with the API key.
Also, not the config
helper in the beforeEach
method. By using the helper, we can set test values for the Google Books service config that will be used in each of our tests.
<?php
use App\Support\ApiRequest;
use App\Support\GoogleBooksApiClient;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\Request;
beforeEach(function () {
Http::fake();
config([
'services.google_books.base_url' => 'https://example.com',
'services.google_books.api_key' => 'foo',
]);
});
it('sets the base url', function () {
$request = ApiRequest::get('foo');
app(GoogleBooksApiClient::class)->send($request);
Http::assertSent(static function (Request $request) {
expect($request)->url()->toStartWith('https://example.com/foo');
return true;
});
});
it('sets the api key as a query parameter', function () {
$request = ApiRequest::get('foo');
app(GoogleBooksApiClient::class)->send($request);
Http::assertSent(static function (Request $request) {
expect($request)->url()->toContain('key=foo');
return true;
});
});
In this article, we covered some helpful steps for integrating third-party APIs in Laravel. By using these simple custom classes, along with the Http
facade, we can ensure all integrations function similarly, are easier to test, and don’t require any project dependencies. In a later post, I will expand on these integration tips by covering DTOs, testing with mock responses, and using API resources.
Thanks for reading!
Laravel News Links