Content Source:
Laravel Forge CLI
Laravel Forge launched their first official command-line tool that gives you a nice set of commands to manage your Forge servers, sites, and more.
The first release (v1.0) of the Forge CLI contains around thirty commands, including initiating deployments, viewing application logs, configuring SSH key authentication, and more.
Installation
You may install the Forge CLI as a global Composer dependency:
composer global require laravel/forge-cli
After it’s installed you can forge from your terminal and see usage:
Get Started
To view a list of all available Forge CLI commands and view the current version of your installation, you may run the forge
command from the command-line:
forge
Authenticating
You will need to generate an API token to interact with the Forge CLI. Tokens are used to authenticate your account without providing personal details. API tokens can be created from Forge’s API dashboard (opens new window).
After you have generated an API token, you should authenticate with your Forge account using the login command:
forge login
Current Server & Switching Servers
When managing Forge servers, sites, and resources via the CLI, you will need to be aware of your currently active server. You may view your current server using the server:current
command. Typically, most of the commands you execute using the Forge CLI will be executed against the active server.
forge server:current
Of course, you may switch your active server at any time. To change your active server, use the server:switch
command:
forge server:switch forge server:switch staging
To view a list of all available servers, you may use the server:list
command:
forge server:list
SSH Key Authentication
Before performing any tasks using the Forge CLI, you should ensure that you have added an SSH key for the forge
user to your servers so that you can securely connect to them. You may have already done this via the Forge UI. You may test that SSH is configured correctly by running the ssh:test
command:
forge ssh:test
To configure SSH key authentication, you may use the ssh:configure
command. The ssh:configure
command accepts a --key
option which instructs the CLI which public key to add to the server. In addition, you may provide a --name
option to specify the name that should be assigned to the key:
forge ssh:configure forge ssh:configure --key=/path/to/public/key.pub --name=sallys-macbook
After you have configured SSH key authentication, you may use the ssh
command to create a secure connection to your server:
forge ssh forge ssh server-name
Sites
To view the list of all available sites, you may use the site:list
command:
forge site:list
Initiating Deployments
One of the primary features of Laravel Forge is deployments. Deployments may be initiated via the Forge CLI using the deploy
command:
forge deploy forge deploy example.com
Updating Environment Variables
You may update a site’s environment variables using the env:pull
and env:push
commands. The env:pull
command may be used to pull down an environment file for a given site:
forge env:pull forge env:pull pestphp.com forge env:pull pestphp.com .env
Once this command has been executed the site’s environment file will be placed in your current directory. To update the site’s environment variables, simply open and edit this file. When you are done editing the variables, use the env:push
command to push the variables back to your site:
forge env:push forge env:push pestphp.com forge env:push pestphp.com .env
If your site is utilizing Laravel’s “configuration caching” feature or has queue workers, the new variables will not be utilized until the site is deployed again.
Viewing Application Logs
You may also view a site’s logs directly from the command-line. To do so, use the site:logs
command:
forge site:logs forge site:logs --follow # View logs in realtime forge site:logs example.com forge site:logs example.com --follow # View logs in realtime
Reviewing Deployment Output / Logs
When a deployment fails, you may review the output / logs via the Forge UI’s deployment history screen. You may also review the output at any time on the command-line using the deploy:logs
command. If the deploy:logs
command is called with no additional arguments, the logs for the latest deployment will be displayed. Or, you may pass the deployment ID to the deploy:logs
command to display the logs for a particular deployment:
forge deploy:logs forge deploy:logs 12345
Running Commands
Sometimes you may wish to run an arbitrary shell command against a site. The command
command will prompt you for the command you would like to run. The command will be run relative to the site’s root directory.
forge command forge command example.com forge command example.com --command="php artisan inspire"
Tinker
As you may know, all Laravel applications include “Tinker” by default. To enter a Tinker environment on a remote server using the Forge CLI, run the tinker
command:
forge tinker forge tinker example.com
Resources
Forge provisions servers with a variety of resources and additional software, such as Nginx, MySQL, etc. You may use the Forge CLI to perform common actions on those resources.
Checking Resource Status
To check the current status of a resource, you may use the {resource}:status
command:
forge daemon:status forge database:status forge nginx:status forge php:status # View PHP logs (default PHP version) forge php:status 8.0 # View PHP 8.0 logs
Viewing Resources Logs
You may also view logs directly from the command-line. To do so, use the {resource}:logs
command:
forge daemon:logs forge daemon:logs --follow # View logs in realtime forge database:logs forge nginx:logs # View error logs forge nginx:logs access # View access logs forge php:logs # View PHP logs (default PHP version) forge php:logs 8.0 # View PHP 8.0 logs
Restarting Resources
Resources may be restarted using the {resource}:restart
command:
forge daemon:restart forge database:restart forge nginx:restart forge php:restart # Restarts PHP (default PHP version) forge php:restart 8.0 # Restarts PHP 8.0
Connecting To Resources Locally
You may use the {resource}:shell
command to quickly access a command line shell that lets you interact with a given resource:
forge database:shell forge database:shell my-database-name forge database:shell my-database-name --user=my-user
For more information and to develop web applications using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”.
To develop custom web apps using Laravel, please visit our technology page.
Content Source:
- laravel-news.com
- forge.laravel.com
What is Application Performance Monitoring (APM)?
Application Performance Monitoring (APM), as the name suggests, is the process of monitoring the performance of the many aspects of your application.
When an end-user logs into your application, for even just one web page to load on their device, there are very many backstage components that need to come together and operate in synchrony to ensure a smooth and fast experience. These include network components (that carry the bytes of data), software components (e.g., server-side frameworks, front-end code, and other dependencies), and hardware components (i.e., CPU processors, memory, and storage of machines that host your web servers, APIs, databases, file systems, etc.) It can become overwhelming to manually keep track of your application performance on all these different levels and across all components. This is even truer when you ideally want monitoring and checks to happen all the time, in real-time!
Well, this is precisely the problem that APM solutions target. APM tools, like ScoutAPM, allow organizations to get a detailed analysis of the performance of your applications, in real-time. This includes critical information about server requests, response times, time-consuming methods and end-points, errors and their root cause analysis, and lots more – presented in a way that is easy to understand and troubleshoot.
These performance insights provide a lot of valuable information about optimizing resource allocations and effective cost reductions while surfacing other issues that could potentially fail your application – and all before the user gets a hint of anything being amiss.
Apart from presenting a bird’s eye view of what is happening within your application as a whole, APM tools provide you with your application’s score on particular metrics that quantify its performance along different grounds.
They provide metrics like request rates, response times, server load, CPU and memory usage, application throughput, server health status, and lots more, enabling organizations to understand what drives their application’s performance or failures.
They bring to light and help you identify performance bottlenecks, memory leaks, bloat, slow database queries, wasted execution cycles, and much more in your application. Additionally, tools like ScoutAPM enable teams to trace the cause of these issues to the specific line of the code causing them so that developers need to spend less time debugging and more time building.
Different platforms, frameworks, and APIs allow you to monitor the performance of a few of your applications’ components – for example, your cloud service provider could provide information about resource usage, logging frameworks could help you capture backend errors and processing times, etc. But wouldn’t it be much more useful to have everything you need under one roof – as a one-stop platform to provide all the information about everything you might need to know about your application’s performance.
Different organizations might want to optimize their application’s performance on different metrics. Some teams might want to prioritize more reliability and uptime, over other applications that might want to focus on higher speeds and lower response times. In this regard, equally important is the amount of flexibility that many of these tools offer in creating customizable dashboards – allowing you to focus on aspects of performance that matter the most to your application.
APM tools, therefore, can go a long way in resolving issues faster, preventing interruptions, boosting performance, increasing business and revenue, and understanding customer interactions.
Let us look at some common use cases of APM solutions to get a pragmatic understanding of how helpful they can be for developers and organizations to ensure that everything about their application is on track.
Common Use Cases for APMs
Use case #1 – Application Development
Application development involves a lot of code tweaking, solving bugs, adding features, experimenting with different libraries and frameworks, refactoring, and so on. This can lead to minor fluctuations in performance that developers might want to track and monitor throughout the development lifecycle and in the staging and production environments.
Therefore, application development can benefit a great deal from the insights provided by APM tools. These could be insights about the application’s performance or an in-depth analysis of issues down to the code level. By highlighting the source of the problem and isolating issues to specific lines (or methods) in the code causing them, these tools narrow down the areas of the project that they should be focusing more on.
Below is an example of code traceability in ScoutAPM, with Github integration enabled.
Use case #2 – Identifying Performance Bottlenecks
A bottleneck in software engineering refers to the negative effect on performance caused by the limited ability or capacity of one component of the system – similar to impeding water flow caused near a bottle’s constricted neck. A bottleneck is like the slower car on a single-track road that keeps everyone else waiting.
Even with the best software and hardware infrastructure in place, all it takes is one sub-optimal component to make your application crawl when it could be flying. APM tools help you identify performance bottlenecks with accuracy. These range from bottlenecks in disk usage, CPU utilization, memory to software and network components. APM platforms like Scout provide a complete analysis of several metrics like the memory allocation, response times, throughput, and error rates corresponding to each end-point in your application. Metrics like these provide insights into the long-term performance of these applications and help highlight where such bottlenecks lie.
It is important to note that if you are just starting out with web development, and working on smaller, personal projects, understanding the importance of APM tools might not come easily or seem super relevant. However, these tools become exponentially more valuable as your application(s) scale-up and cater to hundreds or thousands of users.
For more information and to develop web applications using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”.
To develop custom web apps using Laravel, please visit our technology page.
Content Source:
- laravel-news.com
What will be the best Back End development framework for 2021
What should we learn next? If you are a developer, this question should always be in your mind. Every day, new technologies are introduced and improvements are made to existing ones. Since we can’t learn all these technologies, it is really important to decide what should we learn next.
In this article, we’re going to discuss three back end development frameworks based on three different programming languages to give you an insight into what you should learn in 2021.
Node.js
NodeJS is a JavaScript runtime environment framework that can be used for cross-platform development purposes. Since JavaScript is one of the most popular languages in the current context, that popularity has lifted up NodeJS to be one of the most used back end frameworks as well. Apart from that, NodeJS brings many important features that attract developers.
- NodeJS never waits until an API returns data since it’s asynchronous. NodeJS will move onto the next API request without waiting for the response of the previous one and the notification mechanism of Events of NodeJS takes the responsibility for delivering the response to the server correctly. Hence, NodeJS is known as asynchronous and event-driven.
- Fast code execution.
- No buffer.
- Although NodeJS is single-threaded, high scalability enables it to handle a large number of requests.
NodeJS is used by some famous companies all around the world including eBay, General Electric, GoDaddy, Microsoft, PayPal, Uber, Wikipins. Node JS is a perfect match if you’re building I/O bound applications, data streaming applications, Data Intensive Real-time Applications (DIRT), JSON APIs based applications, or single-page applications.
Advantages
- Based on JavaScript, which is well known to everyone.
- Easy learning curve and large community.
- Contains an excellent package manager.
- Library support.
- Quick and easy handling of concurrent requests.
- Simple and scalable.
- Well established.
Disadvantages
- Some developers may have difficulties working with asynchronous requests.
- Nested callbacks.
Django
Django is an open-source, high-level web application framework written in Python. Django was introduced in 2005, and its idea of using Python for web development was a huge revolution. Django follows the model-template-view architecture and the main focus of this framework is to provide an easy method for the development of complex websites. Instagram, Mozilla, Bitbucket are some leading companies that use Django as their framework.
Advantages
- Rapid Development. One of the main intentions of Django was to reduce the development time of an application.
- Django helps to avoid much common security falls like SQL injection, cross-site scripting, cross-site request forgery, etc.
- Scalability.
- Supports a large set of libraries and helping modules.
- You don’t have to worry much about user authentication and content administration since Django takes care of them for you.
Disadvantages
- Many developers do not have expert knowledge in Python compared to JavaScript.
- Although Django is scalable, there can be issues with small scale applications.
- Monolithic framework.
- Django can’t handle multiple requests simultaneously.
Laravel
PHP is another famous language among web developers and Laravel is based on PHP. Laravel follows model-view-control architecture and is robust and easy to understand. Laravel is known as a good starting point for young developers. It provides a large set of features, like flexible routing for easy scaling, configuration management to handle different environments, query builders and ORM to query databases, Schema Builder to maintain database definitions and schemas, lightweight templates, etc. 9GAG, MasterCard, Kmong are some famous companies that use Laravel in their products.
Advantages
- High Security.
- MVC Based.
Uses blade template engine. - Built-in authorization and authentication systems.
- Supports test automation.
Disadvantages
- Less inbuilt support when compared to Django and NodeJs since Laravel is lightweight.
- Community support is minimized compared to other platforms.
- Converting legacy systems to Laravel is difficult.
- Updating from an older version to a new one may break your application.
- Full-page reloads can be a bit heavy in mobile apps when compared to websites.
NodeJs vs Django vs Laravel
As you can see, all these three frameworks are very popular among developers and they tend to select the framework based on their preferred language most of the time. For example, If you’re good with JavaScript, you will surely go with NodeJS. But there are other aspects we should take into account when selecting a framework.
If you’re a novice developer, who doesn’t have knowledge about JavaScript, Python, or PHP, Django or Python will be a good option for you to start with. Since Python is very straightforward and simple in its syntax, you can easily understand it. So, we will rank Django at the top when it comes to the learning curve while Laravel and NodeJS come next.
Security is another measurement we need to address in any project and all these frameworks provide built-in features to make a developer’s life easy. Out of these three, Django claims first place here as well.
If we talk about scalability and performance Django can be recognized as the best framework in scalability aspects while NodeJS provides the best performance.
All these frameworks have large communities and good documentation to get started with and they’re well established. So don’t hesitate to select them for your projects.
For more information and to develop web application using NodeJS, Hire Back End Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”. To develop custom web apps using NodeJS, please visit our technology page.
Content Source:
- medium.com
What’s new in PHP 8
PHP 8 is here! PHP 8 is a new major improved version, which means that it will introduce performance improvements and lots of new features such as the JIT compiler, union types, attributes, and more.
New features
Let’s start with all new features, it’s quite a list!
Union types
Given the dynamically typed nature of PHP, there are lots of cases where union types can be useful. Union types are a collection of two or more types which indicate that either one of those can be used.
public function foo(Foo|Bar $input): int|float;
Note that void can never be part of a union type, since it indicates “no return value at all”. Furthermore, nullable unions can be written using |null, or by using the existing ? notation:
public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void;
JIT
The JIT — just in time — compiler promises significant performance improvements, albeit not always within the context of web requests. I’ve done my own benchmarks on real-life web applications, and it seems like the JIT doesn’t make that much of a difference, if any, on those kinds of PHP projects.
The nullsafe operator
If you’re familiar with the null coalescing operator you’re already familiar with its shortcomings: it doesn’t work on method calls. Instead you need intermediate checks, or rely on optional helpers provided by some frameworks:
$startDate = $booking->getStartDate(); $dateAsString = $startDate ? $startDate->asDateTimeString() : null;
With the addition of the nullsafe operator, we can now have null coalescing-like behaviour on methods!
$dateAsString = $booking->getStartDate()?->asDateTimeString();
Named arguments
Named arguments allow you to pass in values to a function, by specifying the value name, so that you don’t have to take their order into consideration, and you can also skip optional parameters!
function foo(string $a, string $b, ?string $c = null, ?string $d = null) { /* … */ } foo( b: 'value b', a: 'value a', d: 'value d', );
Attributes
Attributes, commonly known as annotations in other languages, offers a way to add meta data to classes, without having to parse docblocks.
As for a quick look, here’s an example of what attributes look like, from the RFC:
use App\Attributes\ExampleAttribute; #[ExampleAttribute] class Foo { #[ExampleAttribute] public const FOO = 'foo'; #[ExampleAttribute] public $x; #[ExampleAttribute] public function foo(#[ExampleAttribute] $bar) { } }
#[Attribute] class ExampleAttribute { public $value; public function __construct($value) { $this->value = $value; } }
Note that this base Attribute used to be called PhpAttribute in the original RFC, but was changed with another RFC afterwards. If you want to take a deep dive in how attributes work, and how you can build your own; you can read about attributes in depth on this blog.
Match expression
You could call it the big brother of the switch expression: match can return values, doesn’t require break statements, can combine conditions, uses strict type comparisons and doesn’t do any type coercion.
It looks like this:
$result = match($input) { 0 => "hello", '1', '2', '3' => "world", };
Constructor property promotion
This RFC adds syntactic sugar to create value objects or data transfer objects. Instead of specifying class properties and a constructor for them, PHP can now combine them into one.
Instead of doing this:
class Money { public Currency $currency; public int $amount; public function __construct( Currency $currency, int $amount, ) { $this->currency = $currency; $this->amount = $amount; } }
You can now do this:
class Money { public function __construct( public Currency $currency, public int $amount, ) {} }
New static return type
While it was already possible to return self, static wasn’t a valid return type until PHP 8. Given PHP’s dynamically typed nature, it’s a feature that will be useful to many developers.
class Foo { public function test(): static { return new static(); } }
New mixed type
Some might call it a necessary evil: the mixed type causes many to have mixed feelings. There’s a very good argument to make for it though: a missing type can mean lots of things in PHP:
- A function returns nothing or null
- We’re expecting one of several types
- We’re expecting a type that can’t be type hinted in PHP
Because of the reasons above, it’s a good thing the mixed type is added. mixed itself means one of these types:
- array
- bool
- callable
- int
- float
- null
- object
- resource
- string
Note that mixed can also be used as a parameter or property type, not just as a return type.
Also note that since mixed already includes null, it’s not allowed to make it nullable. The following will trigger an error:
// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type. function bar(): ?mixed {}
Throw expression
This RFC changes throw from being a statement to being an expression, which makes it possible to throw exception in many new places:
$triggerError = fn () => throw new MyError(); $foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
Inheritance with private methods
Previously, PHP used to apply the same inheritance checks on public, protected and private methods. In other words: private methods should follow the same method signature rules as protected and public methods. This doesn’t make sense, since private methods won’t be accessible by child classes.
This RFC changed that behaviour, so that these inheritance checks are not performed on private methods anymore. Furthermore, the use of final private function also didn’t make sense, so doing so will now trigger a warning:
Warning: Private methods cannot be final as they are never overridden by other classes
Allowing ::class on objects
A small, yet useful, new feature: it’s now possible to use ::class on objects, instead of having to use get_class() on them. It works the same way as get_class().
$foo = new Foo(); var_dump($foo::class);
Non-capturing catches
Whenever you wanted to catch an exception before PHP 8, you had to store it in a variable, regardless whether you used that variable or not. With non-capturing catches, you can omit the variable, so instead of this:
try { // Something goes wrong } catch (MySpecialException $exception) { Log::error("Something went wrong"); }
You can now do this:
try { // Something goes wrong } catch (MySpecialException) { Log::error("Something went wrong"); }
Note that it’s required to always specify the type, you’re not allowed to have an empty catch. If you want to catch all exceptions and errors, you can use Throwable as the catching type.
Trailing comma in parameter lists
Already possible when calling a function, trailing comma support was still lacking in parameter lists. It’s now allowed in PHP 8, meaning you can do the following:
public function( string $parameterA, int $parameterB, Foo $objectfoo, ) { // … }
Create DateTime objects from interface
You can already create a DateTime object from a DateTimeImmutable object using DateTime::createFromImmutable($immutableDateTime), but the other way around was tricky. By adding DateTime::createFromInterface() and DatetimeImmutable::createFromInterface() there’s now a generalised way to convert DateTime and DateTimeImmutable objects to each other.
DateTime::createFromInterface(DateTimeInterface $other); DateTimeImmutable::createFromInterface(DateTimeInterface $other);
New Stringable interface
The Stringable interface can be used to type hint anything that implements __toString(). Whenever a class implements __toString(), it automatically implements the interface behind the scenes and there’s no need to manually implement it.
class Foo { public function __toString(): string { return 'foo'; } } function bar(string|Stringable $stringable) { /* … */ } bar(new Foo()); bar('abc');
New str_contains() function
Some might say it’s long overdue, but we finally don’t have to rely on strpos() anymore to know whether a string contains another string.
Instead of doing this:
if (strpos('string with lots of words', 'words') !== false) { /* … */ }
You can now do this
if (str_contains('string with lots of words', 'words')) { /* … */ }
New str_starts_with() and str_ends_with() functions
Two other ones long overdue, these two functions are now added in the core.
str_starts_with('haystack', 'hay'); // true str_ends_with('haystack', 'stack'); // true
New fdiv() function
The new fdiv() function does something similar as the fmod() and intdiv() functions, which allows for division by 0. Instead of errors you’ll get INF, -INF or NAN, depending on the case.
New get_debug_type() function
get_debug_type() returns the type of a variable. Sounds like something gettype() would do? get_debug_type() returns more useful output for arrays, strings, anonymous classes and objects.
For example, calling gettype() on a class \Foo\Bar would return object. Using get_debug_type() will return the class name.
A full list of differences between get_debug_type() and gettype() can be found in the RFC.
New get_resource_id() function
Resources are special variables in PHP, referring to external resources. One example is a MySQL connection, another one a file handle.
Each one of those resources gets assigned an ID, though previously the only way to know that id was to cast the resource to int:
$resourceId = (int) $resource;
PHP 8 adds the get_resource_id() functions, making this operation more obvious and type-safe:
$resourceId = get_resource_id($resource);
Abstract methods in traits improvements
Traits can specify abstract methods which must be implemented by the classes using them. There’s a caveat though: before PHP 8 the signature of these method implementations weren’t validated. The following was valid:
trait Test { abstract public function test(int $input): int; } class UsesTrait { use Test; public function test($input) { return $input; } }
PHP 8 will perform proper method signature validation when using a trait and implementing its abstract methods. This means you’ll need to write this instead:
class UsesTrait { use Test; public function test(int $input): int { return $input; } }
Object implementation of token_get_all()
The token_get_all() function returns an array of values. This RFC adds a PhpToken class with a PhpToken::tokenize() method. This implementation works with objects instead of plain values. It consumes less memory and is easier to read.
Variable syntax tweaks
From the RFC: “the Uniform Variable Syntax RFC resolved a number of inconsistencies in PHP’s variable syntax. This RFC intends to address a small handful of cases that were overlooked.”
Type annotations for internal functions externals
Lots of people pitched in to add proper type annotations to all internal functions. This was a long standing issue, and finally solvable with all the changes made to PHP in previous versions. This means that internal functions and methods will have complete type information in reflection.
ext-json always available
Previously it was possible to compile PHP without the JSON extension enabled, this is not possible anymore. Since JSON is so widely used, it’s best developers can always rely on it being there, instead of having to ensure the extension exist first.
For more information and to develop web application using PHP, WordPress OR Laravel, Hire PHP Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”. To develop custom web apps using PHP, Laravel or WordPress, please visit our technology page.
Content Source:
- stitcher.io
Laravel component Media Library
Media Library can associate files with Eloquent models. You can, for instance, associate images with a blog post model. In your Blade view, you can retrieve URLs to the associated images. It can handle multiple collections, work with multiple filesytems, create zips on the fly to download multiple files, use a customized directory structure, save bandwidth using responsive images and much more.
The problem with traditional uploads
Before exploring Media Library Pro, let’s first explained why we built it in the first place. Here’s how a traditional upload form might look like. It uses a regular input of type file.
<form method="POST" enctype="multipart/form-data"> <x-grid> @csrf <x-field label="name"> <x-input id="name" name="name" placeholder="Your first name" /> </x-field> <x-field label="file"> <input type="file" name="file"> @error('file') {{ $message }} @enderror </x-field> <x-button dusk="submit">Submit</x-button> </x-grid> </form>
There are two big problems with this standard upload element.
First, the upload process only starts when the form is submitted. For small files in small forms, this might not be a problem. But imagine you’re uploading a multi MB file in a form. When submitting the form, you now have to wait for the upload to complete before seeing the submission results.
The second problem is something that has bothered us for a long, long time. Imagine that the input field is part of a form of which some fields are required. You’re selecting a file, submitting the form, leaving some of the required fields empty. You get redirected back to the form where error messages are now displayed. Your previous file selection is gone, and you need to select the file again.
Media Library Pro
Media Library Pro is a paid add-on package that offers Blade, Vue, and React components to upload files to your application. It ships with two components. The first one is the attachment component. It is meant to be used on a public-facing page where you want users to upload one or multiple files.
The second one is called the collection component. This one can manage the files in an existing collection. It is meant to be used in the admin part of your app.
Both of these components are available as Vue, React and Blade components. Under the hood, the Blade components are powered by Caleb’s excellent Livewire package.
These components are straightforward to install and are documented in great detail.
Let’s take a look at both the `Attachment` and `Collection` component. In the remainder of the blog post, we’ll use the Blade version of the examples, but rest assured that everything shown can also be done with the Vue and React counterparts.
The Attachment component
To get started with the Attachment Blade component you’ll have to use x-media-library-attachment in your view.
<form method="POST"> @csrf <input id="name" name="name"> <x-media-library-attachment name="avatar"/> <button type="submit">Submit</button> </form>
Here’s how it looks like after we’ve selected a file but before submitting the form.
The x-media-library-attachment has taken care of the upload. The file is now stored as a temporary upload. In case there are validation errors when submitting the form, the x-media-library-attachment will display the temporary upload when you get redirected back to the form. There’s no need for the user to upload the file again.
Here’s the form request used to validate the uploaded.
namespace App\Http\Requests\Blade; use Illuminate\Foundation\Http\FormRequest; use Spatie\MediaLibraryPro\Rules\Concerns\ValidatesMedia; class StoreBladeAttachmentRequest extends FormRequest { use ValidatesMedia; public function rules() { return [ 'name' => 'required', 'media' => ['required', $this->validateSingleMedia() ->maxItemSizeInKb(3000), ], ]; } }
By applying the ValidatesMedia trait, you get access to the validateSingleMedia, which allows you to validate the upload. You can chain on many validation methods, which are documented here.
In your controller, you can associate the upload file to any model you’d like.
$formSubmission ->addFromMediaLibraryRequest($request->media) ->toMediaCollection('images');
And that is all you need to do!
The attachment component can be used to handle multiple uploads as well. In this video, you’ll see how that is done.
The Collection component
You can manage the entire contents of a media library collection with x-media-library-collection component. This
component is intended to use in admin sections.
Here is an example where we will administer an images collection of a $formSubmission model.
<form method="POST"> @csrf <x-field label="name"> <x-input id="name" name="name" autocomplete="off" placeholder="Your name" value="{{ old('name', $formSubmission->name) }}"/> </x-field> <x-field label="Images"> <x-media-library-collection name="images" :model="$formSubmission" collection="images" max-items="3" rules="mimes:png,jpeg" /> </x-field> <x-button dusk="submit" type="submit">Submit</x-button> </form>
Here’s how that component looks like:
This component will display the contents of the entire collection. Files can be added, removed, updated, and reordered.
To validate the response of the form, a form request like this one can be used:
namespace App\Http\Requests\Blade; use Illuminate\Foundation\Http\FormRequest; use Spatie\MediaLibraryPro\Rules\Concerns\ValidatesMedia; class StoreBladeCollectionRequest extends FormRequest { use ValidatesMedia; public function rules() { return [ 'name' => 'required', 'images' => [$this->validateMultipleMedia() ->maxItems(3) ->itemName('required'), ], ]; } }
Again, you need to ValidatesMedia trait. This time the validateMultipleMedia should be used. You can chain on the other validation methods, which are documented here.
In the controller, you can associate the media in the collection component with your model using the syncFromMediaLibraryRequest method.
Here’s the relevant code in the controller of the demo app.
$formSubmission ->syncFromMediaLibraryRequest($request->images) ->toMediaCollection('images');
Adding custom properties
When using the collection component, you probably want to add some extra fields to be displayed. We’ve made this a straightforward thing to do.
In the screenshot below, we added the Extra field field.
You can achieve this by passing a blade view to the fields-view prop of the x-media-library-collection.
<x-media-library-collection name="images" :model="$formSubmission" collection="images" max-items="3" rules="mimes:png,jpeg" fields-view="uploads.blade.partials.custom-properties" />
In that custom-properties view, you can put anything that should be displayed in the right half of the collection component.
Here’s the content of that custom-propertiesview.
@include('media-library::livewire.partials.collection.fields') <div class="media-library-field"> <label class="media-library-label">Extra field</label> <input dusk="media-library-extra-field" class="media-library-input" type="text" {{ $mediaItem->customPropertyAttributes('extra_field') }} /> @error($mediaItem->customPropertyErrorName('extra_field')) <span class="media-library-text-error"> {{ $message }} </span> @enderror </div>
In the form request, you can use the customProperty to validate any extra custom attributes. The second argument of the function can take any validator in Laravel.
namespace App\Http\Requests\Blade; use Illuminate\Foundation\Http\FormRequest; use Spatie\MediaLibraryPro\Rules\Concerns\ValidatesMedia; class StoreBladeCollectionCustomPropertyRequest extends FormRequest { use ValidatesMedia; public function rules() { return [ 'name' => 'required', 'images' => [$this->validateMultipleMedia() ->maxItems(3) ->itemName('required|max:30') ->customProperty('extra_field', 'required|max:30'), ], ]; } }
In the controller where you process the form submission, you should use the withCustomProperties method to whitelist any extra attributes that you want to sync with your media.
$formSubmission ->syncFromMediaLibraryRequest($request->images) ->withCustomProperties('extra_field') ->toMediaCollection('images');
Customizing the look and feel
By default, both the Attachment and Collection components already look good. Probably you’d like to adapt them so they match the look of your app.
Luckily, this is easy to do. The styles that ship with Media Library Pro can be used by importing or linking dist/styles.css. The styles were built with a default tailwind.config.js.
You can customize the styles by importing src/styles.css and run every @apply rule through your own tailwind.config.js
/* app.css */ @tailwind base; @tailwind components; @tailwind utilities; @import "src/styles.css"; …
How temporary files are used
To achieve that behavior where uploaded files are preserved when a form validation error occurs, we use temporary uploads.
Testing the components
Inside the private spatie/laravel-medialibrary-pro repo, there are a lot of tests to make sure the back end integration and the Vue, React, and Blade front end components are working as expected.
We also wanted to have browser tests that ensure that front end components work perfectly with the back end and vice versa. That’s why we added Dusk tests in our demo application. You can see them here.
Let’s take a look at one of them:
/** * @test * * @dataProvider routeNames */ public function it_can_handle_a_single_upload(string $routeName) { $this->browse(function (Browser $browser) use ($routeName) { $browser ->visit(route($routeName)) ->type('name', 'My name') ->attach('@main-uploader', $this->getStubPath('space.png')) ->waitForText('Remove') ->waitUntilMissing('.media-library-progress-wrap.media-library-progress-wrap-loading') ->press('@submit') ->assertSee('Your form has been submitted'); $this->assertCount(1, FormSubmission::get()); $this->assertEquals('space.png', FormSubmission::first()->getFirstMedia('images')->file_name); }); }
This test will upload a file and make sure that the file is associated with a model after the form is submitted.
A thing to note here is that @dataProvider attribute. This will make PHPUnit run the test for each result returned by the routeNames function defined in the same file.
public function routeNames(): array { return [ ['vue.attachment'], ['react.attachment'], ['blade.attachment'], ]; }
You can see that in combination with the routeNames function, the it_can_handle_a_single_upload will run for the vue.attachment, react.attachment and blade.attachment routes. Visiting these routes will display the form that will use the Vue, React, or Blade component, respectively. So, this one test covers a lot of logic. It makes sure that the component work using any technology. This gives us a lot of confidence that all of the components are working correctly.
For more information and to develop web application using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”. To develop custom web apps using React JS, please visit our technology page.
Content Source:
- laravel-news.com
Using Sanctum to authenticate a mobile app
Sanctum is Laravel’s lightweight API authentication package. This tutorial will go over using Laravel Sanctum to authenticate a mobile app. The app will be built in Flutter, Google’s cross-platform app development toolkit. We may skip some implementation details of the mobile app since that is not the focus of this tutorial.
The backend
We’ve set up Homestead to provision a domain name, api.sanctum-mobile.test, where my backend will be served, as well as a MySQL database.
First, create the Laravel app:
laravel new sanctum_mobile
At the time of writing, it gives me a new Laravel project (v8.6.0). As with the SPA tutorial, the API will provide a list of books, so We’ll create the same resources:
php artisan make:model Book -mr
The mr flags create the migration and controller too. Before we mess with the migrations, let’s first install the Sanctum package, since we’ll need its migrations again.
composer require laravel/sanctum php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Now, create the books migration:
Schema::create('books', function (Blueprint $table) { $table->id(); $table->string('title'); $table->string('author'); $table->timestamps(); });
Next, run your app’s migrations:
php artisan migrate
If you now take a look in the database, you’ll see the Sanctum migration has created a personal_access_tokens table, which we’ll use later when authenticating the mobile app.
Let’s update DatabaseSeeder.php to give us some books (and a user for later):
Book::truncate(); $faker = \Faker\Factory::create(); for ($i = 0; $i < 50; $i++) { Book::create([ 'title' => $faker->sentence, 'author' => $faker->name, ]); } User::truncate(); User::create([ 'name' => 'Alex', 'email' => 'alex@alex.com', 'password' => Hash::make('pwdpwd'), ]);
Now seed the database: php artisan db:seed. Finally, create the route and the controller action. Add this to the routes/api.php file:
Route::get('book', [BookController::class, 'index']);
and then in the index method of BookController, return all the books:
return response()->json(Book::all());
After checking that the endpoint works — curl https://api.sanctum-mobile.test/api/book — it’s time to start the mobile app.
The mobile app
For the mobile app, we’ll be using Android Studio and Flutter. Flutter allows you to create cross-platform apps that re-use the same code for Android and iPhone devices. First, follow the instructions to install Flutter and to set up Android Studio, then launch Android Studio and click “Create a new Flutter project.”
Follow the recipe in Flutter’s cookbook to fetch data from the internet to create a page that fetches a list of books from the API. A quick and easy way to expose our API to the Android Studio device is to use Homestead’s share command:
share api.sanctum-mobile.test
The console will output an ngrok page, which will give you a URL (something like https://0c9775bd.ngrok.io) exposing your local server to the public. (An alternative to ngrok is Beyond Code’s Expose.) So let’s create a utils/constants.dart file to put that in:
const API_URL = 'http://191b43391926.ngrok.io';
Now, back to the Flutter cookbook. Create a file books.dart which will contain the classes required for our book list. First, a Book class to hold the data from the API request:
class Book { final int id; final String title; final String author; Book({this.id, this.title, this.author}); factory Book.fromJson(Map<String, dynamic> json) { return Book( id: json['id'], title: json['title'], author: json['author'], ); } }
Second, a BookList class to fetch the books and call the builder to display them:
class BookList extends StatefulWidget { @override _BookListState createState() => _BookListState(); } class _BookListState extends State<BookList> { Future<List<Book>> futureBooks; @override void initState() { super.initState(); futureBooks = fetchBooks(); } Future<List<Book>> fetchBooks() async { List<Book> books = new List<Book>(); final response = await http.get('$API_URL/api/book'); if (response.statusCode == 200) { List<dynamic> data = json.decode(response.body); for (int i = 0; i < data.length; i++) { books.add(Book.fromJson(data[i])); } return books; } else { throw Exception('Problem loading books'); } } @override Widget build(BuildContext context) { return Column( children: <Widget>[ BookListBuilder(futureBooks: futureBooks), ], ); } }
And last, a BookListBuilder to display the books:
class BookListBuilder extends StatelessWidget { const BookListBuilder({ Key key, @required this.futureBooks, }) : super(key: key); final Future<List<Book>> futureBooks; @override Widget build(BuildContext context) { return FutureBuilder<List<Book>>( future: futureBooks, builder: (context, snapshot) { if (snapshot.hasData) { return Expanded(child: ListView.builder( itemCount: snapshot.data.length, itemBuilder: (context, index) { Book book = snapshot.data[index]; return ListTile( title: Text('${book.title}'), subtitle: Text('${book.author}'), ); }, )); } else if (snapshot.hasError) { return Text("${snapshot.error}"); } return CircularProgressIndicator(); } ); } }
Now we just need to modify the MyApp class in main.dart to load the BookList:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sanctum Books', home: new Scaffold( body: BookList(), ) ); } }
Now launch this in your test device or the emulator, and you should see a list of books.
Authentication with Sanctum
Great, so we know the API is working and that we can fetch books from it. The next step is to set up authentication.
We’re going to use the provider package, and follow the guidelines in the official documentation for setting up simple state management. We want to create an authentication provider that keeps track of the logged-in status and eventually communicates with the server. Create a new file, auth.dart. Here’s where the authentication functionality will go. For the moment, we’ll return true so we can test the process works:
class AuthProvider extends ChangeNotifier { bool _isAuthenticated = false; bool get isAuthenticated => _isAuthenticated; Future<bool> login(String email, String password) async { print('logging in with email $email and password $password'); _isAuthenticated = true; notifyListeners(); return true; } }
With this provider, we can now check whether we’re authenticated and display the correct page accordingly. Modify you main function to include the provider:
void main() { runApp( ChangeNotifierProvider( create: (BuildContext context) => AuthProvider(), child: MyApp(), ) ); }
… and modify the MyApp class to show the BookList widget if we’re logged-in, or a LoginForm widget otherwise:
body: Center( child: Consumer<AuthProvider>( builder: (context, auth, child) { switch (auth.isAuthenticated) { case true: return BookList(); default: return LoginForm(); } }, ) ),
The LoginForm classes contain a lot of “widgety” cruft, so We’ll refer you to the GitHub repo if you’re interested in looking at it. Anyway, if you load the app in your test device, you should see a login form. Fill in a random email and password, submit the form, and you’ll see a list of books.
Ok, let’s set up the backend to handle the authentication. The docs tell us to create a route that will accept the username and password, as well as a device name, and return a token. So let’s create a route in the api.php file:
Route::post('token', [AuthController::class, 'requestToken']);
and a controller: php artisan make:controller AuthController. This will contain the code from the docs:
public function requestToken(Request $request): string { $request->validate([ 'email' => 'required|email', 'password' => 'required', 'device_name' => 'required', ]); $user = User::where('email', $request->email)->first(); if (! $user || ! Hash::check($request->password, $user->password)) { throw ValidationException::withMessages([ 'email' => ['The provided credentials are incorrect.'], ]); } return $user->createToken($request->device_name)->plainTextToken; }
Providing the username and password are valid, this will create a token, save it in the database, and return it to the client. To get this to work, we need to add the HasApiTokens trait to our User model. This gives us a tokens relationship, allowing us to create and fetch tokens for the user, and a createToken method. The token itself is a sha256 hash of a 40-character random string: this string (unhashed) is returned to the client, which should save it to use with any future requests to the API. More precisely, the string returned to the client is composed of the token’s id, followed by a pipe character (|), followed by the plain text (unhashed) token.
So now we have this endpoint in place, let’s update the app to use it. The login method will now have to post the email, password, and device_name to this endpoint, and if it gets a 200 response, save the token in the device’s storage. For device_name, We’re using the device_info package to get the device’s unique ID, but in fact, this string is arbitrary.
final response = await http.post('$API_URL/token', body: { 'email': email, 'password': password, 'device_name': await getDeviceId(), }, headers: { 'Accept': 'application/json', }); if (response.statusCode == 200) { String token = response.body; await saveToken(token); _isAuthenticated = true; notifyListeners(); }
We use the shared_preferences package, which allows for the storage of simple key-value pairs, to save the token:
saveToken(String token) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('token', token); }
So now we’ve got the app displaying the books page after a successful login. But of course, as things stand, the books are accessible with or without successful login. Try it out: curl https://api.sanctum-mobile.test/api/book. So now let’s protect the route:
Route:::middleware('auth:sanctum')->get('book', [BookController::class, 'index']);
Login again via the app, and this time you’ll get an error: “Problem loading books”. You are successfully authenticating, but because we don’t as yet send the API token with our request to fetch the books, the API is quite rightly not sending them. As in the previous tutorial, let’s look at the Sanctum guard to see what it’s doing here:
if ($token = $request->bearerToken()) { $model = Sanctum::$personalAccessTokenModel; $accessToken = $model::findToken($token); if (! $accessToken || ($this->expiration && $accessToken->created_at->lte(now()->subMinutes($this->expiration))) || ! $this->hasValidProvider($accessToken->tokenable)) { return; } return $this->supportsTokens($accessToken->tokenable) ? $accessToken->tokenable->withAccessToken( tap($accessToken->forceFill(['last_used_at' => now()]))->save() ) : null; }
The first condition is skipped since we aren’t using the web guard. Which leaves us with the above code. First, it only runs if the request has a “Bearer” token, i.e. if it contains an Authorization header which starts with the string “Bearer”. If it does, it will call the findToken method on the PersonalAccessToken model:
if (strpos($token, '|') === false) { return static::where('token', hash('sha256', $token))->first(); } [$id, $token] = explode('|', $token, 2); if ($instance = static::find($id)) { return hash_equals($instance->token, hash('sha256', $token)) ? $instance : null; }
The first conditional checks to see whether the pipe character is in the token and, if not, to return the first model that matches the token. We assume this is to preserve backward compatibility with versions of Sanctum before 2.3, which did not include the pipe character in the plain text token when returning it to the user. (Here is the pull request: the reason was to make the token lookup query more performant.) Anyway, assuming the pipe character is there, Sanctum grabs the model’s ID and the token itself, and checks to see if the hash matches with what is stored in the database. If it does, the model is returned.
Back in Guard: if no token is returned, or if we’re considering expiring tokens (which we’re not in this case), return null (in which case authentication fails). Finally:
return $this->supportsTokens($accessToken->tokenable) ? $accessToken->tokenable->withAccessToken( tap($accessToken->forceFill(['last_used_at' => now()]))->save() ) : null;
Check that the tokenable model (i.e., the User model) supports tokens (in other words, that it uses the HasApiTokens trait). If not, return null – authentication fails. If so, then return this:
$accessToken->tokenable->withAccessToken( tap($accessToken->forceFill(['last_used_at' => now()]))->save() )
The above example uses the single-argument version of the tap helper. This can be used to force an Eloquent method (in this case, save) to return the model itself. Here the access token model’s last_used_at timestamp is updated. The saved model is then passed as an argument to the User model’s withAccessToken method (which it gets from the HasApiTokens trait). This is a compact way of updating the token’s last_used_at timestamp and returning its associated User model. Which means authentication has been successful.
So, back to the app. With this authentication in place, we need to update the app’s call to the book endpoint to pass the token in the request’s Authorization header. To do this, update the fetchBooks method to grab the token from the Auth provider, then add it to the header:
String token = await Provider.of<AuthProvider>(context, listen: false).getToken(); final response = await http.get('$API_URL/book', headers: { 'Authorization': 'Bearer $token', });
Don’t forget to add a getToken method to the AuthProvider class:
Future<String> getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('token'); }
Now try logging in again, and this time the books should be displayed.
For more information and to develop web application using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”. To develop custom web apps using React JS, please visit our technology page.
Content Source:
- laravel-news.com
Laravel 7.4 – The latest minor release of Laravel 7
Before we move directly to talk about 7.4 release, let’s go through each of the earlier minor releases of Laravel 7.
Laravel 7.1 – Patch to Fix Potential XSS Attacks
We will briefly look at the new features in the 7.1 release, which introduced a convenient API resource method to work with new route caching in Laravel 7.x and the ability to customize the constrained table name.
RouteRegistrar apiResource() Method
Lasse Rafn contributed an apiResource()
convenience method to work with the new Laravel 7.x caching.
Since Laravel 7.X has a new (optimized) routing implemented, route names are more important, and caching routes will break if naming collisions happen.
Normally you can use Route::name(‘posts’)->resource(…… to change the name of a group (useful for nested routes like: /posts/{post}/comments).
However, this is not possible with apiResource
.
We propose this change to allow that. It’s just a convenience to replace:
/ Before Route::name('posts') ->resource( 'posts/{post}/comments', 'PostCommentsController' ) ->only([ 'index', 'show', 'store', 'update', 'destroy' ]); // Using the apiResource() method Route::name('posts') ->apiResource( 'posts/{post}/comments', 'PostCommentsController' );
Customized constrained() Table Name
Samuel França contributed the ability to pass a table name to the constrained() method in the ForeignIdColumnDefinition class:
Here’s one example from the tests:
$blueprint ->foreignId('team_column_id') ->constrained('teams');
Laravel 7.2
This version was released with HTTP client query string support and a new timeout configuration option for the SMTP mail driver. Let’s check out the new features real quick!
The expectsConfirmation Test Method
ShawnCZek contributed the expectsConfirmation()
method on the PendingCommand class used to test artisan commands:
$this->artisan('foo:bar') ->expectsConfirmation('Do you want to continue?', 'no') ->assertExitCode(1);
The confirmation assertion uses expectsQuestion under the hood, but asserts the actual value from the test. The same above would originally need to be:
$this->artisan('foo:bar') ->expectsConfirmation('Do you want to continue?', true) ->assertExitCode(1);
SMTP Mail Driver Timeout
Markus Podar contributed a timeout configuration for the SMTP mail driver. The default is 30 seconds. If you want to tweak the default, add a custom timeout configuration in seconds:
'timeout' => 60, // seconds
HTTP Client Query String Support
Irfaq Syed contributed query string support to the Laravel HTTP Client, meaning you can pass a second argument to Http::get():
Here’s an example of how it works:
Http::get('https://example.com/get'); // URL: https://example.com/get Http::get('https://example.com/get?abc=123'); // URL: https://example.com/get?abc=123 Http::get('https://example.com/get', ['foo' => 'bar']); // URL: https://example.com/get?foo=bar Http::get('https://example.com/get', 'foo=bar'); // URL: https://example.com/get?foo=bar
Note that passing query params to get() overrides any present in the URI, so use one or the other.
Laravel 7.3
This version was released with the ability to use ^4.0 versions of ramsey/uuid. Since the release of Laravel 7.2, a few patch releases are available that we’ll briefly cover.
Ability to use Ramsey UUID V4
Laravel 7.3 adds the possibility to use ^4.0 of ramsey/uuid, but still supports v3.7 as well. The composer dependency is now ^3.7|^4.0.
Component Fixes
Laravel 7.2.2 fixes a few blade component issues. Notably, the make:component
command now supports sub directories:
php artisan make:component Navigation/Item # previously creates the following: # View/Components/Navigation/Item.php # views/components/item.blade.php # Now creates them as expected: # View/Components/Navigation/Item.php # views/components/navigation/item.blade.php
Fix Route Naming Issue
Laravel 7 introduced route caching speed improvements, but with that have been a few issues with apps in-the-wild. Laravel 7.2.1 fixed a route naming issue with cache; you should upgrade to the latest 7.x release to get the newest routing fixes.
It’s important to note that you should ensure the uniqueness of route names, as routes with duplicate names can “cause unexpected behavior in multiple areas of the framework.”
Finally, Laravel 7.4
The latest version is released with quite a few new features, such as a custom model caster interface, a “when” higher-order collection proxy, and the ability to clear an existing “order” from the query builder.
Custom model caster interface, a “when” higher-order collection proxy, and the ability to clear an existing “order” from the query builder are the new features of Laravel 7.4.
Higher Order “when” Proxy for Collections
Loris Leiva contributed the ability to use a higher-order proxy with the Collection::when()
method:
// With this PR, this: $collection->when($condition, function ($collection) use ($item) { $collection->push($item); }); // ... can be refactored as this: $collection->when($condition)->push($item);
This PR enables you to chain other higher-order proxy methods:
// This: $collection->when($condition, function ($collection) { $collection->map->parseIntoSomething(); }); // ... can be refactored as this: $collection->when($condition)->map->parseIntoSomething();
Artisan expectsChoice() Assertion
Adrian Nürnberger contributed a console test method for asking choices.
Given the following example:
$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
You could only assert the message of this question, but cannot test the given choices:
$this->artisan('question') ->expectsQuestion('What is your name?', 'Taylor') ->assertExitCode(0);
As of Laravel 7.4, you can now do the following:
$this->artisan('question') ->expectsChoice('What is your name?', 'Taylor', ['Taylor', 'Dayle']) ->assertExitCode(0);
You can also guarantee the order of choices by passing a fourth boolean argument:
$this->artisan('question') ->expectsChoice('What is your name?', 'Taylor', ['Taylor', 'Dayle'], true) ->assertExitCode(0);
Default Values for the @props Blade Tag
@nihilsen contributed the ability to define default props via @props:
<!-- Previously you might need something like: --> @props(['type', 'message']) @php $type = $type ?? 'info' @endphp <!-- New defaults in Laravel >=7.4 --> @props(['type' => 'info', 'message'])
Castable Interface
Brent Roose contributed a Castable interface which allows “castable” types to specify their caster classes:
// Instead of this class ModelX extends Model { protected $casts = [ 'data' => CastToDTO::class . ':' . MyDTO::class, ]; } // You could do this class ModelY extends Model { protected $casts = [ 'data' => MyDTO::class, ]; } // The underlying class use Illuminate\Contracts\Database\Eloquent\Castable; class MyDTO implements Castable { public static function castUsing() { return CastToDTO::class . ':' . static::class; } }
Remove Orders from the Query Builder
Jonathan Reinink contributed a reorder() method to the query builder to reset orderBy()
calls:
$query = DB::table('users')->orderBy('name'); $unorderedUsers = $query->reorder()->get();
Reorder allows you to define default order in Eloquent relationships, with the ability to back out if needed:
class Account extends Model { public function users() { return $this->hasMany(User::class)->orderBy('name'); } } // Remove the name orderBy and order by email $account->users()->reorder()->orderBy('email'); // The same can be written as: $account->users()->reorder('email');
For more information and to develop web application using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft”. To develop any custom web apps using Laravel, please visit our technology page.
Content Source:
- laravel-news.com
How to develop multi tenant application using Laravel
If you want to run multiple websites using the same Laravel installation while keeping tenant specific data separated for fully independent multi-domain setups then it is pretty easily possible with this multi-tenant Laravel package.
With this package, you can serve multiple websites each with one or more hostnames from the same codebase. It has clear separation of database, assets & it also provides the ability to override logic per tenant.
This is suitable for marketing companies or startups which are building Software as a Service(SaaS).
I know this architecture is opposite to famous micro-service architecture, but just posting it in case someone is in the need to do so.
This package offers following features under the hood:
- Integration with the awesome Laravel framework.
- Event driven, extensible architecture.
- Close – optional – integration into the web server.
- The ability to add tenant specific configs, code, routes etc.
Database separation methods:
- One system database and separated tenant databases (default).
- Table prefixed in the system database.
- Or manually, the way you want, by listening to an event.
Requirements, recommended environment:
- Latest stable and LTS Laravel versions.
- PHP 7+.
- Apache or Nginx.
- MySQL, MariaDB or PostgreSQL.
Installation
composer require hyn/multi-tenant
Automatic service registration
Using auto discovery, the tenancy package will be auto detected by Laravel automatically.
Manual service registration
In case you want to disable webserver integration or prefer manual integration, set the dont-discover
in your application composer.json, like so:
{ "extra": { "laravel": { "dont-discover": [ "hyn/multi-tenant" ] } } }
If you disable auto-discovery you are able to configure the providers by yourself.
Register the service provider in your config/app.php:
'providers' => [ // [..] // Hyn multi tenancy. Hyn\Tenancy\Providers\TenancyProvider::class, // Hyn multi tenancy webserver integration. Hyn\Tenancy\Providers\WebserverProvider::class, ],
Deploy configuration
First publish the configuration and migration files so you can modify it to your needs:
php artisan vendor:publish --tag tenancy
Open the config/tenancy.php
and config/webserver.php
file and modify to your needs.
Make sure your system connection has been configured in database.php
. In case you didn’t override the system connection name the default
connection is used.
Now run:
php artisan migrate --database=system
This will run the required system database migrations.
Testing
Run tests using:
vendor/bin/phpunit
If using MySQL, use:
LIMIT_UUID_LENGTH_32=1 vendor/bin/phpunit
Please be warned running tests will reset your current application completely, dropping tenant and system databases and removing the tenancy.json file inside the Laravel directory.
For more Information and to build a website using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.To develop the custom website using Laravel, please visit our technology page
- fullstackworld.com/
- github.com/tenancy/multi-tenant
Automate Future Notifications and Reminders with Laravel Snooze
Laravel Snooze is a package, which simplifies automating future notifications and reminders in Laravel.
The package’s readme has some typical use-cases for this package:
- Reminder system (1 week before appt, 1 day before, 1 hour before, etc)
- Follow-up surveys (2 days after purchase)
- On-boarding Email Drips (Welcome email after sign-up, additional tips after 3 days, upsell offer after 7 days)
- Short-Term Recurring reports (send every week for the next 4 weeks)
Why use this package?
- Ever wanted to schedule a future notification to go out at a specific time? (was the delayed queue option not enough?)
- Want a simple on-boarding email drip?
- How about happy birthday emails?
Common use cases
- Reminder system (1 week before appt, 1 day before, 1 hour before, etc)
- Follow-up surveys (2 days after purchase)
- On-boarding Email Drips (Welcome email after sign-up, additional tips after 3 days, upsell offer after 7 days)
Short-Term Recurring reports (send every week for the next 4 weeks)
Installation
- Install via composer
- composer require thomasjohnkane/snooze
- php artisan migrate
Publish Configuration File
php artisan vendor:publish --provider="Thomasjohnkane\Snooze\ServiceProvider" --tag="config"
Usage
Using the model trait
Snooze provides a trait for your model, similar to the standard Notifiable trait. It adds a notifyAt()
method to your model to schedule notifications.
use Thomasjohnkane\Snooze\Traits\SnoozeNotifiable; class User extends Model { use SnoozeNotifiable; // ... } // Schedule a birthday notification $user->notifyAt(new BirthdayNotification, Carbon::parse($user->birthday)); // Schedule for a week from now $user->notifyAt(new NextWeekNotification, Carbon::now()->addDays(7)); // Schedule for new years eve $user->notifyAt(new NewYearNotification, Carbon::parse('last day of this year'));
Using the ScheduledNotification::create helper
You can also use the create method on the ScheduledNotification.
ScheduledNotification::create( Auth::user(), // Target new ScheduledNotificationExample($order), // Notification Carbon::now()->addHour() // Send At ); This is also useful for scheduling anonymous notifications (routed direct, rather than on a model). $target = (new AnonymousNotifiable) ->route('mail', 'hello@example.com') ->route('sms', '56546456566'); ScheduledNotification::create( $target, // Target new ScheduledNotificationExample($order), // Notification Carbon::now()->addDay() // Send At );
An important note about scheduling the snooze:send command
Creating a scheduled notification will add the notification to the database. It will be sent by running snooze:send
command at (or after) the stored sendAt
time.
The snooze:send
command is scheduled to run every minute by default. You can change this value (sendFrequency)
in the published config file. Available options are everyMinute
, everyFiveMinutes
, everyTenMinutes
, everyFifteenMinutes
, everyThirtyMinutes
, hourly
, and daily
.
The only thing you need to do is make sure schedule:run
is also running. You can test this by running php artisan schedule:run
in the console.
Setting the send tolerance
If your scheduler stops working, a backlog of scheduled notifications will build up. To prevent users receiving all of the old scheduled notifications at once, the command will only send mail within the configured tolerance. By default this is set to 24 hours, so only mail scheduled to be sent within that window will be sent. This can be configured in the snooze.php
config file.
Cancelling Scheduled Notifications
$notification->cancel();
Note: you cannot cancel a notification that has already been sent.
Rescheduling Scheduled Notifications
$rescheduleAt = Carbon::now()->addDay(1)
$notification->reschedule($rescheduleAt)
Note: you cannot reschedule a notification that has already been sent or cancelled. If you want to duplicate a notification that has already been sent or cancelled, pass a truthy second parameter along with the new send date; reschedule($date, true)
, or use the scheduleAgainAt($date)
method shown below
Duplicate a Scheduled Notification to be sent again
$notification->scheduleAgainAt($newDate);
Check a scheduled notification’s status
// Check if a notification is already cancelled $result = $notification->isCancelled(); // returns a bool // Check if a notification is already sent $result = $notification->isSent(); // returns a bool
Conditionally interrupt a scheduled notification
If you’d like to stop an email from being sent conditionally, you can add the shouldInterrupt()
method to any notification. This method will be checked immediately before the notification is sent.
For example, you might not send a future drip notification if a user has become inactive, or the order the notification was for has been canceled.
public function shouldInterrupt($notifiable) { return $notifiable->isInactive() || $this->order->isCanceled(); }
If this method is not present on your notification, the notification will not be interrupted. Consider creating a shouldInterupt trait if you’d like to repeat conditional logic on groups of notifications.
Running the Tests
composer test
To develop the custom website using Laravel, please visit our technology page
Content Source:
- laravel-news.com
Laravel: Fail, Retry, or Delay a queued job from itself
When creating jobs, listeners, or subscribers to push into the queue, you may start thinking that, once dispatched, you’re all on your own with what the queue worker decides to do with your logic.
Well, it’s not that you can’t interact with the queue worker from inside the job, but you usually don’t need to… until you do.
The magic happens because of the InteractsWithQueue
trait. When the queued job is being pulled out from the queue, the CallQueuedListener
will check if it’s using the InteractsWithQueue
trait, and if it is, the framework will inject the underlying “job” instance inside.
This “job” instance is like the driver that wraps your real Job
class, which contains information about the connection and attempts, among other things.
Context
We will use a transcoding Job
as an example. This is a job that transcodes a podcast audio file into an MP3 of 192kbps. Because this is set in the free transcoding queue, it has limited availability.
Checking the Attempts
The first method is called attempts()
, and as its name implies, it returns the number of attempts. A Job
always starts with one attempt.
This method is meant to be used with others, like fail()
or release()
(delay). For illustration purposes, we will notify the user of the nth retrying: each time we try to transcode a podcast in the free queue, we will notify the user we’re retrying for the nth time, giving him the option to cancel future transcodes.
<?php namespace App\Jobs; use App\Podcast; use Transcoder\Transcoder; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use App\Notifications\PodcastTranscoded; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Notifications\RetyingPodcastTranscode; class TranscodePodcast { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Transcoder Instance * * @var \App\Podcast */ protected $podcast; /** * Create a new Transcode Podcast instance. * * @param \App\Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } /** * Execute the job. * * @param \Transcoder\Transcoder $podcast * @return void */ public function handle(Transcoder $transcoder) { // Tell the user we're retrying for the nth time if ($this->attempts() > 1) { $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); } $transcoded = $this->transcoder->setFile($event->podcast) ->format('mp3') ->bitrate(192) ->start(); // Associate the transcoded podcast to the original podcast. $this->podcast->transcode()->associate($transcoded); // Notify the publisher of the podcast that his podcast is ready $this->publisher->notify(new PodcastTranscoded($this->podcast)); } }
Telling the user that we’re retrying something for the nth time is useful when the logic has failed beforehand, letting the user (or developer) check what went wrong, but of course you can do more than that.
Personally, we like to do that after the Job
failed, and if it has retries left, tell him that we’re going to retry later.
Deleting the Job
The second method is delete()
. As you can guess, you can delete the current Job
from the queue.
This can be handy when you shouldn’t process the job or listener after it was queued for several reasons. For example, think about this scenario: the publisher that uploaded the podcast has been deactivated for any reason (like a TOS violation) before the transcoding occurs, and we should not process the podcast.
We will add that code to the example from before:
<?php namespace App\Jobs; use App\Podcast; use Transcoder\Transcoder; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use App\Notifications\PodcastTranscoded; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Notifications\RetyingPodcastTranscode; class TranscodePodcast { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Transcoder Instance * * @var \App\Podcast */ protected $podcast; /** * Create a new Transcode Podcast instance. * * @param \App\Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } /** * Execute the job. * * @param \Transcoder\Transcoder $podcast * @return void */ public function handle(Transcoder $transcoder) { // If the publisher has been deactivated, take this job out if ($this->podcast->publisher->isDeactivated()) { $this->delete(); } // Tell the user we're retrying for the nth time if ($this->attempts() > 1) { $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); } $transcoded = $this->transcoder->setFile($event->podcast) ->format('mp3') ->bitrate(192) ->start(); // Associate the transcoded podcast to the original podcast. $this->podcast->transcode()->associate($transcoded); // Notify the publisher of the podcast that his podcast is ready $this->publisher->notify(new PodcastTranscoded($this->podcast)); } }
If you need to delete the job on models that may have been deleted, you may want to set $deleteWhenMissingModels
to true to avoid processing something that is not there.
Failing the Job
This is very, very handy when you need control over artificially failing the logic, because using an empty return
statement will mark the Job as done successfully. You can forcefully fail the queued job
, hopefully with an exception, allowing the handler to retry it later if possible.
This gives you finer control when the job fails. In any case, you can also use the failed()
method, which allows you to perform any cleaning after it fails, like notifying the user or deleting something.
In this example, we will fail the job if the podcast cannot be retrieved from storage for whatever reason, like when the CDN goes down, throwing a custom exception.
<?php namespace App\Jobs; use App\Podcast; use Transcoder\Transcoder; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use App\Exceptions\PodcastUnretrievable; use App\Notifications\PodcastTranscoded; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Notifications\RetyingPodcastTranscode; class TranscodePodcast { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Transcoder Instance * * @var \App\Podcast */ protected $podcast; /** * Create a new Transcode Podcast instance. * * @param \App\Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } /** * Execute the job. * * @param \Transcoder\Transcoder $podcast * @return void */ public function handle(Transcoder $transcoder) { // If the publisher has been deactivated, take this job out if ($this->podcast->publisher->isDeactivated()) { $this->delete(); } // If the podcast cannot be retrieved from the storage, we will fail miserably. if ($this->podcast->fileDoesntExists()) { $this->fail(new PodcastUnretrievable($this->podcast)); } // Tell the user we're retrying for the nth time if ($this->attempts() > 1) { $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); } $transcoded = $this->transcoder->setFile($event->podcast) ->format('mp3') ->bitrate(192) ->start(); // Associate the transcoded podcast to the original podcast. $this->podcast->transcode()->associate($transcoded); // Notify the publisher of the podcast that his podcast is ready $this->publisher->notify(new PodcastTranscoded($this->podcast)); } }
Releasing (Delaying) a Job
This is probably the most useful method of the trait, as it allows you to push the job further into the future. This method is used for rate limiting your job.
Apart from rate limiting, you can also use this when something is not available but you expect it to be in the near future. Also, to avoid failing preemptively.
In this last example, we will delay the transcoding for later: if the transcoder is under heavy usage, we will delay the transcoding for five minutes until the load lowers.
<?php namespace App\Jobs; use App\Podcast; use Transcoder\Transcoder; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use App\Exceptions\PodcastUnretrievable; use App\Notifications\PodcastTranscoded; use Illuminate\Queue\InteractsWithQueue; use App\Notifications\TranscoderHighUsage; use Illuminate\Foundation\Bus\Dispatchable; use App\Notifications\RetyingPodcastTranscode; class TranscodePodcast { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Transcoder Instance * * @var \App\Podcast */ protected $podcast; /** * Create a new Transcode Podcast instance. * * @param \App\Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } /** * Execute the job. * * @param \Transcoder\Transcoder $podcast * @return void */ public function handle(Transcoder $transcoder) { // If the publisher has been deactivated, take this job out if ($this->podcast->publisher->isDeactivated()) { $this->delete(); } // If the podcast cannot be retrieved from the storage, we will fail miserably. if ($this->podcast->fileDoesntExists()) { $this->fail(new PodcastUnretrievable($this->podcast)); } // If the Transcoder usage is high, we will avoid choking it by delaying the // transcoding by 5 minutes. Otherwise we may risk stalling the transcoder // process, which will take down all transcoding sub-processes with him. if ($transcoder->getLoad()->isHigh()) { $delay = 60 * 5; $this->podcast->publisher->notify(new TranscoderHighUsage($this->podcast, $delay)); $this->release($delay); } // Tell the user we're retrying for the nth time if ($this->attempts() > 1) { $this->podcast->publisher->notify(new RetryingPodcastTranscode($this->podcast, $this->attempts()); } $transcoded = $this->transcoder->setFile($event->podcast) ->format('mp3') ->bitrate(192) ->start(); // Associate the transcoded podcast to the original podcast. $this->podcast->transcode()->associate($transcoded); // Notify the publisher of the podcast that his podcast is ready $this->publisher->notify(new PodcastTranscoded($this->podcast)); } }
We could use some magic, for example, getting assign some slots to the transcoder, and delay the job if the transcoder slots are full.
And that’s pretty much all you can do from inside the queued job.
For more Information and to build a website using Laravel, Hire Laravel Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.
To develop the custom website using Laravel, please visit our technology page
Content Source:
- medium.com