Pro level tips and tricks for Laravel developers.
via Laravel News Links
16 Laravel Pro Tips and Tricks
Building a Vue SPA With Laravel Part 6
Building a Vue SPA With Laravel Part 6
We are going to finish the last part of basic CRUD: creating new users. You have all the tools you need from the previous topics we’ve covered thus far, so feel free to try to work on creating users and comparing this article to your efforts.
If you need to catch up, we left off in Part 5 with the ability to delete users and how to redirect users after successful deletion. We also looked at extracting our HTTP client to a dedicated module for reuse across the application.
Here’s the series outline thus far:
Adding the Create Users Component
First up, we’re going to create and configure the frontend component for creating new users. As a reminder, this tutorial isn’t focused on permissions; we are using the built-in Laravel users table to demonstrate working with CRUD within the context of a Vue Router project.
The UsersCreate.vue
component is similar to the UsersEdit.vue
component we created in Part 4:
<template> <div> <h1>Create a User</h1> <div v-if="message" class="alert"></div> <form @submit.prevent="onSubmit($event)"> <div class="form-group"> <label for="user_name">Name</label> <input id="user_name" v-model="user.name" /> </div> <div class="form-group"> <label for="user_email">Email</label> <input id="user_email" type="email" v-model="user.email" /> </div> <div class="form-group"> <label for="user_password">Password</label> <input id="user_password" type="password" v-model="user.password" /> </div> <div class="form-group"> <button type="submit" :disabled="saving"> </button> </div> </form> </div> </template> <script> import api from '../api/users'; export default { data() { return { saving: false, message: false, user: { name: '', email: '', password: '', } } }, methods: { onSubmit($event) { this.saving = true this.message = false } } } </script> <style lang="scss" scoped> $red: lighten(red, 30%); $darkRed: darken($red, 50%); .form-group { margin-bottom: 1em; label { display: block; } } .alert { background: $red; color: $darkRed; padding: 1rem; margin-bottom: 1rem; width: 50%; border: 1px solid $darkRed; border-radius: 5px; } </style>
We added the form and inputs and stubbed out an onSubmit
method. The rest of the component is identical to the UsersEdit
component, except for the addition of the password
input. A password is required to create a new user. We skipped having a password field when editing a user because typically, you have a specific password change flow that is separate from editing a user.
Note that we could spend some time extracting the form in both the create and edit views to a dedicated component, but we will leave that for another time (or feel free to work on that independently). The only difference is populating the form with existing user data (including user ID) vs. an empty form for creating users.
Configuring the Route
Next, we need to configure the Vue route and link to the page so we can navigate to the user creation screen. Open the resources/assets/js/app.js
file and add the following route (and import):
import UsersCreate from './views/UsersCreate'; // ... const router = new VueRouter({ mode: 'history', routes: [ // ... { path: '/users/create', name: 'users.create', component: UsersCreate, }, { path: '/404', name: '404', component: NotFound }, { path: '*', redirect: '/404' }, ], });
Next, let’s add the link to the new component in the assets/js/views/UsersIndex.vue
component:
<template> <div class="users"> <!-- ... --> <div> <router-link :to="{ name: 'users.create' }">Add User</router-link> </div> </div> </template>
You should now be able to recompile your frontend assets with yarn watch
and see the following:
Submitting the Form
At this point, we don’t have a backend route, so submitting the form via the API client will return a 405 Method Not Allowed
. Let’s wire up the onSubmit()
handler in the UsersCreate
component without defining the route, which will allow us to see the error state of submitting the form quickly:
methods: { onSubmit($event) { this.saving = true this.message = false api.create(this.user) .then((data) => { console.log(data); }) .catch((e) => { this.message = e.response.data.message || 'There was an issue creating the user.'; }) .then(() => this.saving = false) } }
Our form logs out the response data at this point, catches errors and then finally toggles saving = false
to hide the “saving” state. We attempt to read the message
property from the response or provide a default error message.
Next, we need to add the create()
method to the API module we import in the component located at resources/assets/js/api/users.js
:
export default { // ... create(data) { return client.post('users', data); }, // ... };
The form will send a POST
request to the UsersController
via the client. If you submit the form, you will see an error message with a 405
response error in the console:
Adding the API Endpoint
We are ready to add the API endpoint in Laravel for creating a new user. It will be similar to editing an existing user. However, this response will return a 201 Created
status code.
We will start by defining the route for storing a new user via the API:
// routes/api.php Route::namespace('Api')->group(function () { // ... Route::post('/users', 'UsersController@store'); });
Next, open up the app/Http/Controllers/UsersController.php
file and add store()
method:
public function store(Request $request) { $data = $request->validate([ 'name' => 'required', 'email' => 'required|unique:users', 'password' => 'required|min:8', ]); return new UserResource(User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => bcrypt($data['password']), ])); }
When a user is valid, the new user response looks similar to the following when you submit the form:
{ "data": { "id":51, "name":"Paul Redmond", "email":"paul@example.com" } }
If you submit invalid data, you will get something similar the following message:
Handing Success
We already handle what happens with a server error or a validation error; let’s finish up by handling a successful user creation. We’ll clear the form and redirect to the user’s edit page:
onSubmit($event) { this.saving = true this.message = false api.create(this.user) .then((response) => { this.$router.push({ name: 'users.edit', params: { id: response.data.data.id } }); }) .catch((e) => { this.message = e.response.data.message || 'There was an issue creating the user.'; }) .then(() => this.saving = false) }
Here’s the final UsersCreate.vue
component:
<template> <div> <h1>Create a User</h1> <div v-if="message" class="alert"></div> <form @submit.prevent="onSubmit($event)"> <div class="form-group"> <label for="user_name">Name</label> <input id="user_name" v-model="user.name" /> </div> <div class="form-group"> <label for="user_email">Email</label> <input id="user_email" type="email" v-model="user.email" /> </div> <div class="form-group"> <label for="user_password">Password</label> <input id="user_password" type="password" v-model="user.password" /> </div> <div class="form-group"> <button type="submit" :disabled="saving"> </button> </div> </form> </div> </template> <script> import api from '../api/users'; export default { data() { return { saving: false, message: false, user: { name: '', email: '', password: '', } } }, methods: { onSubmit($event) { this.saving = true this.message = false api.create(this.user) .then((response) => { this.$router.push({ name: 'users.edit', params: { id: response.data.data.id } }); }) .catch((e) => { this.message = e.response.data.message || 'There was an issue creating the user.'; }) .then(() => this.saving = false) } } } </script> <style lang="scss" scoped> $red: lighten(red, 30%); $darkRed: darken($red, 50%); .form-group { margin-bottom: 1em; label { display: block; } } .alert { background: $red; color: $darkRed; padding: 1rem; margin-bottom: 1rem; width: 50%; border: 1px solid $darkRed; border-radius: 5px; } </style>
Conclusion
We have a basic working form to create new users that only have basic validation logic. This tutorial walks you through the basics of doing CRUD in Vue.
As homework, you can define a dedicated user form component for rendering a form for creating a new user and editing existing users if you think it’d be valuable reuse. We are okay with the duplication for now but would be good practice for creating reusable components.
I’d also like to emphasize that I stripped out many nice things we could have done, such as using a CSS framework like Bootstrap, etc. I decided to focus on the core aspects of someone that has never worked with Vue Router or building a single page application before. While to some, the tutorial might feel trivial, to beginners, it focuses on some essential concepts that differ from building traditional server-side applications.
Filed in: News
Enjoy this? Get Laravel News delivered straight to your inbox every Sunday.
No Spam, ever. We’ll never share your email address and you can opt out at any time.
6 Things to Do Before a Product Launch
The best way to bring much-needed attention to your new product or service is by doing a product launch.
Even with an awesome product, a great deal of its success depends on the launch, as the hype surrounding it can bring you more sales, more happy customers, and a better reputation for your company.
Still, having a good product launch is a daunting task, which takes a lot of research and planning in order to be successful and if you don’t go through all the key steps to do it right, you are risking the future of your product.
Here are a few things you shouldn’t skip doing when launching your new product.
Research the market
For a new product to be successful in the market, you should do your research well. There are three key points that need to be considered even before developing the product.
- Audience. No product will succeed if no one wants to use it, so think about your potential customers and their needs. Is there a pain point your product will solve?
- Competition. Are there any other businesses offering the same or similar product? How are you going to differentiate yourself from them? What makes your product unique and special?
- Finances. How much will you need to invest upfront and what are the risks attached to the new product launch?
Once you’ve found the answers to these questions, you will be able to create both a unique selling proposition and a value proposition.
This means you will be able to provide your customers with answers on why your product is better than the one your competitors are offering and what they’ll get in return for their money.
Plan ahead
For a product launch to succeed, one must plan ahead.
There are numerous aspects which need to be included in the plan, from product development and testing to marketing and pricing.
Not all the details have to be known at the early stages of the planning, but the timeline and the framework of the activities should be mapped out and defined.
As gaining attention is your main goal, you will need to gear up early and focus on building relationships with influencers, as well as brainstorm all the creative elements which are going to get you publicity. Making a schedule will make it easier for your teams to check their progress and prioritize tasks, and if followed, a plan will decrease the odds of working under stress and launching late.
Find the right influencers
With all the impact social media channels have nowadays, you should focus on finding influencers who can easily approach your target market.
Stats say that 49% of consumers rely on the influencer recommendation on social media, so be sure to provide influencers with the exclusive news and samples of your products.
Encourage, entice, and incentivize them to put their reviews of your products on their blogs, video, and social media channels. This can bring the right kind of attention to your launch and attract the right customers.
See Also: 5 Reasons Why You Should Consider Instagram Advertising
Promote the launch
Before the product hits the market, you need to educate your potential customers on its features and advantages and build hype around its arrival. Here are some ideas on how to best achieve this:
- Update your social media followers regularly on your progress.
- Write a blog and educate your customers on products features.
- Use videos, demos and whitepapers to give detailed info on your product.
- Submit guest posts to sites relevant in your industry.
- Have social media contests offering your customers a chance to be the first to get a sample of your product.
- Provide the industry experts and influencers with samples of your product and freebies.
- Make a countdown timer on your website and publish teaser videos and photos.
If possible, it is the best to tie your launch day to a larger event, such as an industry event or trade show, to get the most publicity for launching.
Get feedback
The best way to know whether your product is going to be a success once it reaches the market is by soft-launching it to a portion of your target audience. By testing your product in your product market, you will gather valuable information. You can then use this to make the necessary adjustments to your product or campaign.
There are many different ways to get customer feedback that can help you solve the customers’ complaints and issues from the moment your new product reaches the market. Statistics show that a dissatisfied customer shares their experience with 16 people on average, while only 1 out of 26 will actually complain to the company. By using different social media listening and other available tools, you can now respond to both the positive and negative feedback in a timely manner and do wonders for your brand’s reputation.
Keep the fire alive
The launch is hardly over when the launch day is done.
To get the most out of it, plan ahead on how to best exploit it afterward.
You can achieve this by posting the news about your latest product and the benefits it brings into your customers’ lives. Also, think about the ways to involve your customers into this post-launch campaign and motivate them to post videos and photos of themselves using and enjoying your product.
As a successful launch is essential to the positioning of your product in the market and your brand’s reputation, make sure to plan it carefully and not leave anything out. Use as many promotional tactics you can handle, because when it comes to a product launch, you can not over-market it.
The post 6 Things to Do Before a Product Launch appeared first on Dumb Little Man.
via Dumb Little Man – Tips for Life
6 Things to Do Before a Product Launch
The First Mulan Trailer Teases a Sweeping Warrior’s Adventure
The first trailer for Disney’s live-action remake of Mulan is here, and it’s gorgeous.
In a sweeping ninety seconds, the trailer showcases big, beautiful settings and an attention to detail that makes the remake look more like a period film than a Disney adaptation. Mulan, played by Yifeu Lu, goes from quiet daughter to epic warrior. The trailer teases really sweeping fights, too, real Wuxia-type stuff, and you can absolutely see that stylistic influence on the shooting here.
So far as music goes, there’s no singing, but hints of the animated film’s music are heard in the soundtrack. Which, if that’s the approach the movie takes, is fairly understated for what looks to be a pretty self-serious film.
Check out the first trailer, and get ready: Mulan comes to theaters March 27, 2020.
For more, make sure you’re following us on our new Instagram @io9dotcom.
via Gizmodo
The First Mulan Trailer Teases a Sweeping Warrior’s Adventure
Laravel Prequel (Beta)
Laravel Prequel 0.5.0-beta
What is Laravel Prequel exactly?
Laravel Prequel is meant to be a database management tool to replace the need for separate standalone database tools like phpMyAdmin, Sequel Pro or MySQL Workbench. With its (hopefully) clear and concise UI, Prequel is to be a modern and lightweight database browser/tool ready for the web of the future. Prequel’s design is purposefully based on that of Laravel Telescope because (web-)developers today have enough to learn and master already, so let’s help eachother out and make sure to not add anything virtually useless to that huge pile of knowledge.
Clear and concise database management
Laravel Prequel (Beta)
Laravel Prequel has entered v0.5.0-beta, that means I deemed it ready enough to be tested by the public. But note that a beta release is still a beta release and is not a stable release so it is definitely not recommended to be used in production environments.
Luckily, Prequel has taken precautions, Prequel automatically disables itself in a production environment as people looking directly into your database is – let’s just say – not ideal.
Installation (the beta release way)
To install follow the instructions below.
$ composer require protoqol/prequel $ php artisan vendor:publish --tag=config $ php artisan vendor:publish --tag=public
When installation and publishing is done navigate to /prequel
in your browser to see Prequel in action!
Issues, bugs and feature requests can be reported here!
Docs coming soon!
Credits
License
The MIT License (MIT). Please see License File for more information.
New Features for Querying Polymorphic Relations in Laravel 5.8.27
New Features for Querying Polymorphic Relations in Laravel 5.8.27
The Laravel team released v5.8.27 with a whereHasMorph()
method to work with polymorphic Eloquent relationships (MorphTo
).
The new whereHasMorph()
and corresponding methods make it possible to query polymorphic relationships with something like the following:
Comment::whereHasMorph('commentable', [Post::class, Video::class], function ($query) { $query->where('title', 'foo'); })->get();
Which produces something like the following query:
select * from "comments" where ( ( "commentable_type" = 'App\Post' and exists ( select * from "posts" where "comments"."commentable_id" = "posts"."id" and "title" = 'foo' ) ) or ( "commentable_type" = 'App\Video' and exists ( select * from "videos" where "comments"."commentable_id" = "videos"."id" and "title" = 'foo' ) ) )
All the examples shown here are from the pull request (#28928) description, which explains it in further detail, so check out the full notes:
By creating a temporary
BelongsTo
relationship for each type and allowing relationship instances as the first argument ofhas()
, we can reuse most of the existing code.
The type is passed as the second argument to the closure, which simplifies queries with different constraints:
Comment::whereHasMorph('commentable', [Post::class, Video::class], function ($query, $type) { if ($type === Post::class) { // $query-> } if ($type === Video::class) { // $query-> } });
Last, providing a wildcard as the second argument will let Laravel get the possible types from the database:
Comment::whereHasMorph('commentable', '*', function ($query) { $query->where('title', 'foo'); })->get();
Next, the mix asset URL is configurable via the MIX_ASSET_URL
environment variable.
Next, you can set the RedisManager
default driver with the setDriver()
method.
You can see the full list of fixes below, and the whole diff between 5.8.26 and 5.8.27 on GitHub. The full release notes for Laravel 5.8 are available in the GitHub 5.8 changelog:
v5.8.27
Added
- Let
mix
helper useapp.mix_url
config (#28952) - Added
RedisManager::setDriver()
method (#28985) - Added
whereHasMorph()
and corresponding methods to work withMorphTo
relations (#28928)
Fixed
Changed
- Prevented
TestResponse::dump()
andTestResponse::dumpHeaders()
methods from ending execution of the script (#28960) - Allowed
TestResponse::dump()
andTestResponse::dumpHeaders()
methods chaining (#28967) - Allowed to
NotificationFake
accept custom channels (#28969) - Replace contents of service manifest atomically (#28973)
- Pass down the
serverVersion
database connection option to Doctrine DBAL connection (#28964, 1b55b28) - Replace
self::
withstatic::
in theRelation::getMorphedModel()
(#28974) - Set a message for
SuspiciousOperationException
(#29000) - Storing Mailgun Message-ID in the headers after sending (#28994)
Filed in: News
Enjoy this? Get Laravel News delivered straight to your inbox every Sunday.
No Spam, ever. We’ll never share your email address and you can opt out at any time.
via Laravel News
New Features for Querying Polymorphic Relations in Laravel 5.8.27
Intro Guide to Dockerfile Best Practices
There are over one million Dockerfiles on GitHub today, but not all Dockerfiles are created equally. Efficiency is critical, and this blog series will cover five areas for Dockerfile best practices to help you write better Dockerfiles: incremental build time, image size, maintainability, security and repeatability. If you’re just beginning with Docker, this first blog post is for you! The next posts in the series will be more advanced.
Important note: the tips below follow the journey of ever-improving Dockerfiles for an example Java project based on Maven. The last Dockerfile is thus the recommended Dockerfile, while all intermediate ones are there only to illustrate specific best practices.
Incremental build time
In a development cycle, when building a Docker image, making code changes, then rebuilding, it is important to leverage caching. Caching helps to avoid running build steps again when they don’t need to.
Tip #1: Order matters for caching
However, the order of the build steps (Dockerfile instructions) matters, because when a step’s cache is invalidated by changing files or modifying lines in the Dockerfile, subsequent steps of their cache will break. Order your steps from least to most frequently changing steps to optimize caching.
Tip #2: More specific COPY to limit cache busts
Only copy what’s needed. If possible, avoid “COPY .” When copying files into your image, make sure you are very specific about what you want to copy. Any changes to the files being copied will break the cache. In the example above, only the pre-built jar application is needed inside the image, so only copy that. That way unrelated file changes will not affect the cache.
Tip #3: Identify cacheable units such as apt-get update & install
Each RUN instruction can be seen as a cacheable unit of execution. Too many of them can be unnecessary, while chaining all commands into one RUN instruction can bust the cache easily, hurting the development cycle. When installing packages from package managers, you always want to update the index and install packages in the same RUN: they form together one cacheable unit. Otherwise you risk installing outdated packages.
Reduce Image size
Image size can be important because smaller images equal faster deployments and a smaller attack surface.
Tip #4: Remove unnecessary dependencies
Remove unnecessary dependencies and do not install debugging tools. If needed debugging tools can always be installed later. Certain package managers such as apt, automatically install packages that are recommended by the user-specified package, unnecessarily increasing the footprint. Apt has the –no-install-recommends flag which ensures that dependencies that were not actually needed are not installed. If they are needed, add them explicitly.
Tip #5: Remove package manager cache
Package managers maintain their own cache which may end up in the image. One way to deal with it is to remove the cache in the same RUN instruction that installed packages. Removing it in another RUN instruction would not reduce the image size.
There are further ways to reduce image size such as multi-stage builds which will be covered at the end of this blog post. The next set of best practices will look at how we can optimize for maintainability, security, and repeatability of the Dockerfile.
Maintainability
Tip #6: Use official images when possible
Official images can save a lot of time spent on maintenance because all the installation stamps are done and best practices are applied. If you have multiple projects, they can share those layers because they use exactly the same base image.
Tip #7: Use more specific tags
Do not use the latest tag. It has the convenience of always being available for official images on Docker Hub but there can be breaking changes over time. Depending on how far apart in time you rebuild the Dockerfile without cache, you may have failing builds.
Instead, use more specific tags for your base images. In this case, we’re using openjdk. There are a lot more tags available so check out the Docker Hub documentation for that image which lists all the existing variants.
Tip #8: Look for minimal flavors
Some of those tags have minimal flavors which means they are even smaller images. The slim variant is based on a stripped down Debian, while the alpine variant is based on the even smaller Alpine Linux distribution image. A notable difference is that debian still uses GNU libc while alpine uses musl libc which, although much smaller, may in some cases cause compatibility issues. In the case of openjdk, the jre flavor only contains the java runtime, not the sdk; this also drastically reduces the image size.
Reproducibility
So far the Dockerfiles above have assumed that your jar artifact was built on the host. This is not ideal because you lose the benefits of the consistent environment provided by containers. For instance if your Java application depends on specific libraries it may introduce unwelcome inconsistencies depending on which computer the application is built.
Tip #9: Build from source in a consistent environment
The source code is the source of truth from which you want to build a Docker image. The Dockerfile is simply the blueprint.
You should start by identifying all that’s needed to build your application. Our simple Java application requires Maven and the JDK, so let’s base our Dockerfile off of a specific minimal official maven image from Docker Hub, that includes the JDK. If you needed to install more dependencies, you could do so in a RUN step.
The pom.xml and src folders are copied in as they are needed for the final RUN step that produces the app.jar application with mvn package
. (The -e flag is to show errors and -B to run in non-interactive aka “batch” mode).
We solved the inconsistent environment problem, but introduced another one: every time the code is changed, all the dependencies described in pom.xml are fetched. Hence the next tip.
Tip #10: Fetch dependencies in a separate step
By again thinking in terms of cacheable units of execution, we can decide that fetching dependencies is a separate cacheable unit that only needs to depend on changes to pom.xml and not the source code. The RUN step between the two COPY steps tells Maven to only fetch the dependencies.
There is one more problem that got introduced by building in consistent environments: our image is way bigger than before because it includes all the build-time dependencies that are not needed at runtime.
Tip #11: Use multi-stage builds to remove build dependencies (recommended Dockerfile)
Multi-stage builds are recognizable by the multiple FROM statements. Each FROM starts a new stage. They can be named with the AS keyword which we use to name our first stage “builder” to be referenced later. It will include all our build dependencies in a consistent environment.
The second stage is our final stage which will result in the final image. It will include the strict necessary for the runtime, in this case a minimal JRE (Java Runtime) based on Alpine. The intermediary builder stage will be cached but not present in the final image. In order to get build artifacts into our final image, use COPY --from=STAGE_NAME
. In this case, STAGE_NAME is builder.
Multi-stage builds is the go-to solution to remove build-time dependencies.
We went from building bloated images inconsistently to building minimal images in a consistent environment while being cache-friendly. In the next blog post, we will dive more into other uses of multi-stage builds.
New blog from #Docker: Intro guide to Dockerfile best practices
Click To Tweet
Additional Resources:
- DockerCon Talk Recording: Dockerfile Best Practices
- DockerCon Talk Slides: Dockerfile Best Practices
- Docker Docs: Dockerfile References
The post Intro Guide to Dockerfile Best Practices appeared first on Docker Blog.
How to make any Laravel application multi-tenant in 5 minutes
We will be implementing a multi-database tenancy package of mine, stancl/tenancy
into a simple Laravel blog example I found on GitHub.
The main feature of the package is that you don’t have to make any changes to your app’s code. After it identifies the tenant using the hostname, it automatically configures database, Redis, cache and the filesystem for that tenant.
This tutorial explains the basics of making an app multi-tenant using this package. For a real-world production application, you should read the documentation. It’s not long.
Installing the tenancy package
The package supports Laravel 5.7 and 5.8.
You will also need Redis and phpredis.
composer require stancl/tenancy
Adding middleware
Open app/Http/Kernel.php
and make the Stancl\Tenancy\Middleware\InitializeTenancy
middleware top priority, to make sure everything is configured for tenancy before any code is run.
protected $middlewarePriority = [ \Stancl\Tenancy\Middleware\InitializeTenancy::class, // ... ];
Making the routes multi-tenant
The package lets you have tenant routes in routes/tenant.php
and shared routes in routes/web.php
. When someone visits a tenant route, the InitializeMiddleware
will configure everything for tenancy. If you have some content you don’t want to apply tenany for, such as landing pages, sign up pages, etc, put those into web.php
.
Since we don’t have any shared content, we’ll just rename routes/web.php
to routes/tenant.php
and create an empty routes/web.php
file.
Configuration
First you need to publish the configuration file of the package. Run
php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=config
and you should see something along the lines of Copied File [...] to [/config/tenancy.php]
.
This lets us make changes to the configuration file of the tenancy package. I don’t want to use MySQL for this simple repository, so I will change the DB driver for the application in .env
and for the package in config/tenancy.php
:
// .env DB_CONNECTION=sqlite // config/tenancy.php 'database' => [ 'based_on' => 'sqlite', // ... ],
Creating a Redis connection
By default, the package uses Redis as the "central" storage — for storing data about tenants. Redis is ideal for this thanks to its high performance. You don’t really need to use a relational database for this.
Add this to database.redis
config:
'tenancy' => [ 'host' => env('TENANCY_REDIS_HOST', '127.0.0.1'), 'password' => env('TENANCY_REDIS_PASSWORD', null), 'port' => env('TENANCY_REDIS_PORT', 6379), 'database' => env('TENANCY_REDIS_DB', 3), ],
Make sure you use a unique database
number — Redis supports 16 databases which let you have multiple applications use the same Redis instance without any conflicts.
Creating tenants
We’ll create two tenants to see that the data separation works correctly. We’ll be using php artisan tinker
to keep it simple. For your app, you should create a page that does this.
We’ll use these two subdomains:
- tenant1.localhost
- tenant2.localhost
Anything under the .localhost
TLD is automatically redirected to 127.0.0.1
, so we don’t have to make any changes to /etc/hosts
.
Open php artisan tinker
and run these two functions:
>>> tenant()->create('tenant1.localhost') => [ "uuid" => "e5611150-9a9e-11e9-8315-b9eb127de2b8", "domain" => "tenant1.localhost", ] >>> tenant()->create('tenant2.localhost') => [ "uuid" => "e8002ec0-9a9e-11e9-8095-51e64ce28359", "domain" => "tenant2.localhost", ]
Migrations
We’ll move all migrations from database/migrations
to database/migrations/tenant
so that we can run them for our tenants.
touch database/migrations/tenant mv database/migrations/*.php database/migrations/tenant
Now we run php artisan tenants:migrate
:
php artisan tenants:migrate Tenant: e8002ec0-9a9e-11e9-8095-51e64ce28359 (tenant2.localhost) Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table Migrating: 2017_03_04_131126_create_posts_table Migrated: 2017_03_04_131126_create_posts_table Migrating: 2017_03_04_131334_create_categories_table Migrated: 2017_03_04_131334_create_categories_table Migrating: 2017_03_04_131558_create_tags_table Migrated: 2017_03_04_131558_create_tags_table Migrating: 2017_03_04_131702_create_post_tag_table Migrated: 2017_03_04_131702_create_post_tag_table Migrating: 2017_03_04_131909_create_comments_table Migrated: 2017_03_04_131909_create_comments_table Migrating: 2017_03_04_133429_add_columns_to_user Migrated: 2017_03_04_133429_add_columns_to_user Tenant: e5611150-9a9e-11e9-8315-b9eb127de2b8 (tenant1.localhost) Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table Migrating: 2017_03_04_131126_create_posts_table Migrated: 2017_03_04_131126_create_posts_table Migrating: 2017_03_04_131334_create_categories_table Migrated: 2017_03_04_131334_create_categories_table Migrating: 2017_03_04_131558_create_tags_table Migrated: 2017_03_04_131558_create_tags_table Migrating: 2017_03_04_131702_create_post_tag_table Migrated: 2017_03_04_131702_create_post_tag_table Migrating: 2017_03_04_131909_create_comments_table Migrated: 2017_03_04_131909_create_comments_table Migrating: 2017_03_04_133429_add_columns_to_user Migrated: 2017_03_04_133429_add_columns_to_user
Note: If you’re using the same sample blog repository as I chose, you might have to tweak the 2017_03_04_133429_add_columns_to_user
migration (make the API key nullable). This is not an issue with the tenancy package but with the blog repository. I have submitted a PR so by the time you’re reading this, this should no longer be an issue.
Seeding the database
php artisan tenants:seed --class DummyDataSeeder Tenant: e8002ec0-9a9e-11e9-8095-51e64ce28359 (tenant2.localhost) Database seeding completed successfully. Tenant: e5611150-9a9e-11e9-8315-b9eb127de2b8 (tenant1.localhost) Database seeding completed successfully.
Visiting the sites
I didn’t want to set up Apache or Nginx just for a simple demonstration, so I used:
php artisan serve
Now we can visit the sites. If we visit tenant1.localhost:8000
and tenant2.localhost:8000
, we can see two same applications, running from the same source code, but with different content.
First blog
Second blog
Closing
My package makes it very easy to implement multi-tenancy into any Laravel application in a way that the application code doesn’t have to be aware of any tenancy. The magic is automatically identifying tenants based on the subdomain when a route in the routes/tenant.php
file is visited (those routes have the InitializeTenancy
middleware applied on them) and then accordingly switching the database and Redis connections and making some changes to the cache and filesystem.
If you’re building a multi-tenant app, I highly recommend using this package. Implementing tenancy on your own can be painful, which is why I created this package — any Laravel application I make can be made multi-tenant in 5 minutes using this package.
Thanks for reading. If you have any questions or suggestions about the package, feel free to open Issues on the GitHub page.
via Laravel News Links
How to make any Laravel application multi-tenant in 5 minutes
Danny DeVito and Danny Glover Dive Into the Hilarious First Trailer for Jumanji: The Next Level
If you thought Dwayne Johnson channeling a teenage boy in the video game version of “Jumaaanjee,” you ain’t seen nothing yet. Check out the first trailer for Jumanji: The Next Level.
Jumanji: The Next Level is the sequel to the surprise hit Jumanji: Welcome to the Jungle and the story picks up some time after the events of the previous movie, when four teenagers ended up in the bodies of video game avatars (played by Johnson, Kevin Hart, Karen Gillan, and Jack Black), forced to play the game to get out alive.
Spencer (Alex Wolff) has seemingly gone missing inside the now-malfunctioning video game, and it’s up to his three friends to go in after him. Once our heroes step inside, they quickly realize something’s wrong. Bethany’s nowhere to be found, characters are switching places…and two old guys have shown up! That’s right, Danny DeVito and Danny Glover are playing the game too. DeVito’s character is in Johnson’s avatar, and Hart is channeling Glover.
I will say: Watching Johnson and Hart play two old guys is more of a delight than I anticipated. Johnson’s got that Nor’Easter accent down. However, it’ll be sad not seeing Black perfectly channeling Bethany, as Fridge (Ser’Darius Blain) is now in his body.
Jumanji: The Next Level sees the return of Nick Jonas, Madison Iseman, and Morgan Turner, along with a new character played by Awkwafina. The movie debuts on December 13.
For more, make sure you’re following us on our new Instagram @io9dotcom.
via Gizmodo
Danny DeVito and Danny Glover Dive Into the Hilarious First Trailer for Jumanji: The Next Level
Florida Sheriff: ‘If You Need to Shoot Somebody, Shoot ’em a Lot’
Polk County, FL — County Sheriff Grady Judd is a peace officer who knows and appreciates the value of armed citizens. In this short video, he encourages people to get guns and become good at using them.
If you’re not afraid of a gun, get one. Become proficient. Get a concealed firearms license and carry it.
And if you need to shoot somebody, shoot ’em a lot.
He also notes that “the armed assailant doesn’t plan on you fighting back. He plans on having a gun, doing all the shooting, and you’re just the sitting duck.”
Well the ducks need to shoot back.
Good advice. It worked for the guy who was attacked at a gas pump last month. He put one bad guy in the hospital and walked away. Don’t you just love a happy ending?
Enjoy the video; it won’t take much of your time.
Sheriff Judd: Prepare yourself, fight back
Sheriff Judd says everyone needs to prepare for an active shooter. If you’re comfortable with a gun, arm yourself, he says. "And if you need to shoot somebody, shoot ’em a lot." MORE: fox13news.com/news/local-news/262968643-story
Posted by FOX 13 News – Tampa Bay on Wednesday, June 21, 2017
The post Florida Sheriff: ‘If You Need to Shoot Somebody, Shoot ’em a Lot’ appeared first on AllOutdoor.com.
via All Outdoor
Florida Sheriff: ‘If You Need to Shoot Somebody, Shoot ’em a Lot’