Eloquent: Getting Started
- Introduction
- Generating Model Classes
- Eloquent Model Conventions
- Retrieving Models
- Retrieving Single Models / Aggregates
- Inserting & Updating Models
- Deleting Models
- Replicating Models
- Query Scopes
- Comparing Models
- Events
Introduction
Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well.
{tip} Before getting started, be sure to configure a database connection in your application's
config/database.php
configuration file. For more information on configuring your database, check out the database configuration documentation.
Generating Model Classes
To get started, let's create an Eloquent model. Models typically live in the app\Models
directory and extend the Illuminate\Database\Eloquent\Model
class. You may use the make:model
Artisan command to generate a new model:
php artisan make:model Flight
If you would like to generate a database migration when you generate the model, you may use the --migration
or -m
option:
php artisan make:model Flight --migration
You may generate various other types of classes when generating a model, such as factories, seeders, and controllers. In addition, these options may be combined to create multiple classes at once:
# Generate a model and a FlightFactory class...
php artisan make:model Flight --factory
php artisan make:model Flight -f
# Generate a model and a FlightSeeder class...
php artisan make:model Flight --seed
php artisan make:model Flight -s
# Generate a model and a FlightController class...
php artisan make:model Flight --controller
php artisan make:model Flight -c
# Generate a model and a migration, factory, seeder, and controller...
php artisan make:model Flight -mfsc
Eloquent Model Conventions
Models generated by the make:model
command will be placed in the app/Models
directory. Let's examine a basic model class and discuss some of Eloquent's key conventions:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
//
}
Table Names
After glancing at the example above, you may have noticed that we did not tell Eloquent which database table corresponds to our Flight
model. By convention, the "snake case", plural name of the class will be used as the table name unless another name is explicitly specified. So, in this case, Eloquent will assume the Flight
model stores records in the flights
table, while an AirTrafficController
model would store records in an air_traffic_controllers
table.
If your model's corresponding database table does not fit this convention, you may manually specify the model's table name by defining a table
property on the model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'my_flights';
}
Primary Keys
Eloquent will also assume that each model's corresponding database table has a primary key column named id
. If necessary, you may define a protected $primaryKey
property on your model to specify a different column that serves as your model's primary key:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}
In addition, Eloquent assumes that the primary key is an incrementing integer value, which means that Eloquent will automatically cast the primary key to an integer. If you wish to use a non-incrementing or a non-numeric primary key you must define a public $incrementing
property on your model that is set to false
:
<?php
class Flight extends Model
{
/**
* Indicates if the model's ID is auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
}
If your model's primary key is not an integer, you should define a protected $keyType
property on your model. This property should have a value of string
:
<?php
class Flight extends Model
{
/**
* The data type of the auto-incrementing ID.
*
* @var string
*/
protected $keyType = 'string';
}
Timestamps
By default, Eloquent expects created_at
and updated_at
columns to exist on your model's corresponding database table. Eloquent will automatically set these column's values when models are created or updated. If you do not want these columns to be automatically managed by Eloquent, you should define a $timestamps
property on your model with a value of false
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}
If you need to customize the format of your model's timestamps, set the $dateFormat
property on your model. This property determines how date attributes are stored in the database as well as their format when the model is serialized to an array or JSON:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The storage format of the model's date columns.
*
* @var string
*/
protected $dateFormat = 'U';
}
If you need to customize the names of the columns used to store the timestamps, you may define CREATED_AT
and UPDATED_AT
constants on your model:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}
Database Connections
By default, all Eloquent models will use the default database connection that is configured for your application. If you would like to specify a different connection that should be used when interacting with a particular model, you should define a $connection
property on the model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'sqlite';
}
Default Attribute Values
By default, a newly instantiated model instance will not contain any attribute values. If you would like to define the default values for some of your model's attributes, you may define an $attributes
property on your model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The model's default values for attributes.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
Retrieving Models
Once you have created a model and its associated database table, you are ready to start retrieving data from your database. You can think of each Eloquent model as a powerful query builder allowing you to fluently query the database table associated with the model. The model's all
method will retrieve all of the records from the model's associated database table:
use App\Models\Flight;
foreach (Flight::all() as $flight) {
echo $flight->name;
}
Building Queries
The Eloquent all
method will return all of the results in the model's table. However, since each Eloquent model serves as a query builder, you may add additional constraints to queries and then invoke the get
method to retrieve the results:
$flights = Flight::where('active', 1)
->orderBy('name')
->take(10)
->get();
{tip} Since Eloquent models are query builders, you should review all of the methods provided by Laravel's query builder. You may use any of these methods when writing your Eloquent queries.
Refreshing Models
If you already have an instance of an Eloquent model that was retrieved from the database, you can "refresh" the model using the fresh
and refresh
methods. The fresh
method will re-retrieve the model from the database. The existing model instance will not be affected:
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();
The refresh
method will re-hydrate the existing model using fresh data from the database. In addition, all of its loaded relationships will be refreshed as well:
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"
Collections
As we have seen, Eloquent methods like all
and get
retrieve multiple records from the database. However, these methods don't return a plain PHP array. Instead, an instance of Illuminate\Database\Eloquent\Collection
is returned.
The Eloquent Collection
class extends Laravel's base Illuminate\Support\Collection
class, which provides a variety of helpful methods for interacting with data collections. For example, the reject
method may be used to remove models from a collection based on the results of an invoked closure:
$flights = Flight::where('destination', 'Paris')->get();
$flights = $flights->reject(function ($flight) {
return $flight->cancelled;
});
In addition to the methods provided by Laravel's base collection class, the Eloquent collection class provides a few extra methods that are specifically intended for interacting with collections of Eloquent models.
Since all of Laravel's collections implement PHP's iterable interfaces, you may loop over collections as if they were an array:
foreach ($flights as $flight) {
echo $flight->name;
}
Chunking Results
Your application may run out of memory if you attempt to load tens of thousands of Eloquent records via the all
or get
methods. Instead of using these methods, the chunk
method may be used to process large numbers of models more efficiently.
The chunk
method will retrieve a subset of Eloquent models, passing them to a closure for processing. Since only the current chunk of Eloquent models is retrieved at a time, the chunk
method will provide significantly reduced memory usage when working with a large amount of models:
use App\Models\Flight;
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
The first argument passed to the chunk
method is the number of records you wish to receive per "chunk". The closure passed as the second argument will be invoked for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the closure.
If you are filtering the results of the chunk
method based on a column that you will also be updating while iterating over the results, you should use the chunkById
method. Using the chunk
method in these scenarios could lead to unexpected and inconsistent results. Internally, the chunkById
method will always retrieve models with an id
column greater than the last model in the previous chunk:
Flight::where('departed', true)
->chunkById(200, function ($flights) {
$flights->each->update(['departed' => false]);
}, $column = 'id');
Cursors
Similar to the chunk
method, the cursor
method may be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records.
The cursor
method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. Internally, the cursor
method uses PHP generators to implement this functionality:
use App\Models\Flight;
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
//
}
The cursor
returns an Illuminate\Support\LazyCollection
instance. Lazy collections allow you to use many of the collection methods available on typical Laravel collections while only loading a single model into memory at a time:
use App\Models\User;
$users = User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Advanced Subqueries
Subquery Selects
Eloquent also offers advanced subquery support, which allows you to pull information from related tables in a single query. For example, let's imagine that we have a table of flight destinations
and a table of flights
to destinations. The flights
table contains an arrived_at
column which indicates when the flight arrived at the destination.
Using the subquery functionality available to the query builder's select
and addSelect
methods, we can select all of the destinations
and the name of the flight that most recently arrived at that destination using a single query:
use App\Models\Destination;
use App\Models\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();
Subquery Ordering
In addition, the query builder's orderBy
function supports subqueries. Continuing to use our flight example, we may use this functionality to sort all destinations based on when the last flight arrived at that destination. Again, this may be done while executing a single database query:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
)->get();
Retrieving Single Models / Aggregates
In addition to retrieving all of the records matching a given query, you may also retrieve single records using the find
, first
, or firstWhere
methods. Instead of returning a collection of models, these methods return a single model instance:
use App\Models\Flight;
// Retrieve a model by its primary key...
$flight = Flight::find(1);
// Retrieve the first model matching the query constraints...
$flight = Flight::where('active', 1)->first();
// Alternative to retrieving the first model matching the query constraints...
$flight = Flight::firstWhere('active', 1);
Sometimes you may wish to retrieve the first result of a query or perform some other action if no results are found. The firstOr
method will return the first result matching the query or, if no results are found, execute the given closure. The value returned by the closure will be considered the result of the firstOr
method:
$model = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});
Not Found Exceptions
Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers. The findOrFail
and firstOrFail
methods will retrieve the first result of the query; however, if no result is found, an Illuminate\Database\Eloquent\ModelNotFoundException
will be thrown:
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', '>', 3)->firstOrFail();
If the ModelNotFoundException
is not caught, a 404 HTTP response is automatically sent back to the client:
use App\Models\Flight;
Route::get('/api/flights/{id}', function ($id) {
return Flight::findOrFail($id);
});
Retrieving Or Creating Models
The firstOrCreate
method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the attributes resulting from merging the first array argument with the optional second array argument:
The firstOrNew
method, like firstOrCreate
, will attempt to locate a record in the database matching the given attributes. However, if a model is not found, a new model instance will be returned. Note that the model returned by firstOrNew
has not yet been persisted to the database. You will need to manually call the save
method to persist it:
use App\Models\Flight;
// Retrieve flight by name or create it if it doesn't exist...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
// Retrieve flight by name or create it with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Retrieve flight by name or instantiate a new Flight instance...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
// Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Retrieving Aggregates
When interacting with Eloquent models, you may also use the count
, sum
, max
, and other aggregate methods provided by the Laravel query builder. As you might expect, these methods return a scalar value instead of an Eloquent model instance:
$count = Flight::where('active', 1)->count();
$max = Flight::where('active', 1)->max('price');
Inserting & Updating Models
Inserts
Of course, when using Eloquent, we don't only need to retrieve models from the database. We also need to insert new records. Thankfully, Eloquent makes it simple. To insert a new record into the database, you should instantiate a new model instance and set attributes on the model. Then, call the save
method on the model instance:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Flight;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* Store a new flight in the database.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
}
In this example, we assign the name
field from the incoming HTTP request to the name
attribute of the App\Models\Flight
model instance. When we call the save
method, a record will be inserted into the database. The model's created_at
and updated_at
timestamps will automatically be set when the save
method is called, so there is no need to set them manually.
Alternatively, you may use the create
method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the create
method:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
However, before using the create
method, you will need to specify either a fillable
or guarded
property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default. To learn more about mass assignment, please consult the mass assignment documentation.
Updates
The save
method may also be used to update models that already exist in the database. To update a model, you should retrieve it and set any attributes you wish to update. Then, you should call the model's save
method. Again, the updated_at
timestamp will automatically be updated, so there is no need to manually set its value:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();
Mass Updates
Updates can also be performed against models that match a given query. In this example, all flights that are active
and have a destination
of San Diego
will be marked as delayed:
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
The update
method expects an array of column and value pairs representing the columns that should be updated.
{note} When issuing a mass update via Eloquent, the
saving
,saved
,updating
, andupdated
model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
Examining Attribute Changes
Eloquent provides the isDirty
, isClean
, and wasChanged
methods to examine the internal state of your model and determine how its attributes have changed from when the model was originally retrieved.
The isDirty
method determines if any of the model's attributes have been changed since the model was retrieved. You may pass a specific attribute name to the isDirty
method to determine if a particular attribute is dirty. The isClean
will determine if an attribute has remained unchanged since the model was retrieved. This method also accepts an optional attribute argument:
use App\Models\User;
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
The wasChanged
method determines if any attributes were changed when the model was last saved within the current request cycle. If needed, you may pass an attribute name to see if a particular attribute was changed:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false
The getOriginal
method returns an array containing the original attributes of the model regardless of any changes to the model since it was retrieved. If needed, you may pass a specific attribute name to get the original value of a particular attribute:
$user = User::find(1);
$user->name; // John
$user->email; //<EMAIL_ADDRESS>$user->name = "Jack";
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...
Mass Assignment
You may use the create
method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the method:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
However, before using the create
method, you will need to specify either a fillable
or guarded
property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default.
A mass assignment vulnerability occurs when a user passes an unexpected HTTP request field and that field changes a column in your database that you did not expect. For example, a malicious user might send an is_admin
parameter through an HTTP request, which is then passed to your model's create
method, allowing the user to escalate themselves to an administrator.
So, to get started, you should define which model attributes you want to make mass assignable. You may do this using the $fillable
property on the model. For example, let's make the name
attribute of our Flight
model mass assignable:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
}
Once you have specified which attributes are mass assignable, you may use the create
method to insert a new record in the database. The create
method returns the newly created model instance:
$flight = Flight::create(['name' => 'London to Paris']);
If you already have a model instance, you may use the fill
method to populate it with an array of attributes:
$flight->fill(['name' => 'Amsterdam to Frankfurt']);
Mass Assignment & JSON Columns
When assigning JSON columns, each column's mass assignable key must be specified in your model's $fillable
array. For security, Laravel does not support updating nested JSON attributes when using the guarded
property:
/**
* The attributes that are mass assignable.
*
* @var array
*/
$fillable = [
'options->enabled',
];
Allowing Mass Assignment
If you would like to make all of your attributes mass assignable, you may define your model's $guarded
property as an empty array. If you choose to unguard your model, you should take special care to always hand-craft the arrays passed to Eloquent's fill
, create
, and update
methods:
/**
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = [];
Upserts
Occasionally, you may need to update an existing model or create a new model if no matching model exists. Like the firstOrCreate
method, the updateOrCreate
method persists the model, so there's no need to manually call the save
method.
In the example below, if a flight exists with a departure
location of Oakland
and a destination
location of San Diego
, it's price
and discounted
columns will be updated. If no such flight exists, a new flight will be created which has the attributes resulting from merging the first argument array with the second argument array:
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
If you would like to perform multiple "upserts" in a single query, then you should use the upsert
method instead. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of the columns that should be updated if a matching record already exists in the database. The upsert
method will automatically set the created_at
and updated_at
timestamps if timestamps are enabled on the model:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
{note} All databases systems except SQL Server require the columns in the second argument provided to the
upsert
method to have a "primary" or "unique" index.
Deleting Models
To delete a model, you may call the delete
method on the model instance:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->delete();
Deleting An Existing Model By Its Primary Key
In the example above, we are retrieving the model from the database before calling the delete
method. However, if you know the primary key of the model, you may delete the model without explicitly retrieving it by calling the destroy
method. In addition to accepting the single primary key, the destroy
method will accept multiple primary keys, an array of primary keys, or a collection of primary keys:
Flight::destroy(1);
Flight::destroy(1, 2, 3);
Flight::destroy([1, 2, 3]);
Flight::destroy(collect([1, 2, 3]));
{note} The
destroy
method loads each model individually and calls thedelete
method so that thedeleting
anddeleted
events are properly dispatched for each model.
Deleting Models Using Queries
Of course, you may build an Eloquent query to delete all models matching your query's criteria. In this example, we will delete all flights that are marked as inactive. Like mass updates, mass deletes will not dispatch model events for the models that are deleted:
$deletedRows = Flight::where('active', 0)->delete();
{note} When executing a mass delete statement via Eloquent, the
deleting
anddeleted
model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement.
Soft Deleting
In addition to actually removing records from your database, Eloquent can also "soft delete" models. When models are soft deleted, they are not actually removed from your database. Instead, a deleted_at
attribute is set on the model indicating the date and time at which the model was "deleted". To enable soft deletes for a model, add the Illuminate\Database\Eloquent\SoftDeletes
trait to the model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Flight extends Model
{
use SoftDeletes;
}
{tip} The
SoftDeletes
trait will automatically cast thedeleted_at
attribute to aDateTime
/Carbon
instance for you.
You should also add the deleted_at
column to your database table. The Laravel schema builder contains a helper method to create this column:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Facades\Schema;
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});
Now, when you call the delete
method on the model, the deleted_at
column will be set to the current date and time. However, the model's database record will be left in the table. When querying a model that uses soft deletes, the soft deleted models will automatically be excluded from all query results.
To determine if a given model instance has been soft deleted, you may use the trashed
method:
if ($flight->trashed()) {
//
}
Restoring Soft Deleted Models
Sometimes you may wish to "un-delete" a soft deleted model. To restore a soft deleted model, you may call the restore
method on a model instance. The restore
method will set the model's deleted_at
column to null
:
$flight->restore();
You may also use the restore
method in a query to restore multiple models. Again, like other "mass" operations, this will not dispatch any model events for the models that are restored:
Flight::withTrashed()
->where('airline_id', 1)
->restore();
The restore
method may also be used when building relationship queries:
$flight->history()->restore();
Permanently Deleting Models
Sometimes you may need to truly remove a model from your database. You may use the forceDelete
method to permanently remove a soft deleted model from the database table:
$flight->forceDelete();
You may also use the forceDelete
method when building Eloquent relationship queries:
$flight->history()->forceDelete();
Querying Soft Deleted Models
Including Soft Deleted Models
As noted above, soft deleted models will automatically be excluded from query results. However, you may force soft deleted models to be included in a query's results by calling the withTrashed
method on the query:
use App\Models\Flight;
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();
The withTrashed
method may also be called when building a relationship query:
$flight->history()->withTrashed()->get();
Retrieving Only Soft Deleted Models
The onlyTrashed
method will retrieve only soft deleted models:
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();
Replicating Models
You may create an unsaved copy of an existing model instance using the replicate
method. This method is particularly useful when you have model instances that share many of the same attributes:
use App\Models\Address;
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
Query Scopes
Global Scopes
Global scopes allow you to add constraints to all queries for a given model. Laravel's own soft delete functionality utilizes global scopes to only retrieve "non-deleted" models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints.
Writing Global Scopes
Writing a global scope is simple. First, define a class that implements the Illuminate\Database\Eloquent\Scope
interface. Laravel does not have a conventional location that you should place scope classes, so you are free to place this class in any directory that you wish.
The Scope
interface requires you to implement one method: apply
. The apply
method may add where
constraints or other types of clauses to the query as needed:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AncientScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('created_at', '<', now()->subYears(2000));
}
}
{tip} If your global scope is adding columns to the select clause of the query, you should use the
addSelect
method instead ofselect
. This will prevent the unintentional replacement of the query's existing select clause.
Applying Global Scopes
To assign a global scope to a model, you should override the model's booted
method and invoke the model's addGlobalScope
method. The addGlobalScope
method accepts an instance of your scope as its only argument:
<?php
namespace App\Models;
use App\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope(new AncientScope);
}
}
After adding the scope in the example above to the App\Models\User
model, a call to the User::all()
method will execute the following SQL query:
select * from `users` where `age` > 200
Anonymous Global Scopes
Eloquent also allows you to define global scopes using closures, which is particularly useful for simple scopes that do not warrant a separate class of their own. When defining a global scope using a closure, you should provide a scope name of your own choosing as the first argument to the addGlobalScope
method:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope('ancient', function (Builder $builder) {
$builder->where('created_at', '<', now()->subYears(2000));
});
}
}
Removing Global Scopes
If you would like to remove a global scope for a given query, you may use the withoutGlobalScope
method. This method accepts the class name of the global scope as its only argument:
User::withoutGlobalScope(AncientScope::class)->get();
Or, if you defined the global scope using a closure, you should pass the string name that you assigned to the global scope:
User::withoutGlobalScope('ancient')->get();
If you would like to remove several or even all of the query's global scopes, you may use the withoutGlobalScopes
method:
// Remove all of the global scopes...
User::withoutGlobalScopes()->get();
// Remove some of the global scopes...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
Local Scopes
Local scopes allow you to define common sets of query constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, prefix an Eloquent model method with scope
.
Scopes should always return a query builder instance:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include popular users.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
/**
* Scope a query to only include active users.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeActive($query)
{
return $query->where('active', 1);
}
}
Utilizing A Local Scope
Once the scope has been defined, you may call the scope methods when querying the model. However, you should not include the scope
prefix when calling the method. You can even chain calls to various scopes:
use App\Models\User;
$users = User::popular()->active()->orderBy('created_at')->get();
Combining multiple Eloquent model scopes via an or
query operator may require the use of closures to achieve the correct logical grouping:
$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
However, since this can be cumbersome, Laravel provides a "higher order" orWhere
method that allows you to fluently chain scopes together without the use of closures:
$users = App\Models\User::popular()->orWhere->active()->get();
Dynamic Scopes
Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope method's signature. Scope parameters should be defined after the $query
parameter:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope a query to only include users of a given type.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}
Once the expected arguments have been added to your scope method's signature, you may pass the arguments when calling the scope:
$users = User::ofType('admin')->get();
Comparing Models
Sometimes you may need to determine if two models are the "same". The is
method may be used to quickly verify two models have the same primary key, table, and database connection:
if ($post->is($anotherPost)) {
//
}
The is
method is also available when using the belongsTo
, hasOne
, morphTo
, and morphOne
relationships. This method is particularly helpful when you would like to compare a related model without issuing a query to retrieve that model:
if ($post->author()->is($user)) {
//
}
Events
Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: retrieved
, creating
, created
, updating
, updated
, saving
, saved
, deleting
, deleted
, restoring
, restored
, and replicating
.
The retrieved
event will dispatch when an existing model is retrieved from the database. When a new model is saved for the first time, the creating
and created
events will dispatch. The updating
/ updated
events will dispatch when an existing model is modified and the save
method is called. The saving
/ saved
events will dispatch when a model is created or updated.
To start listening to model events, define a $dispatchesEvents
property on your Eloquent model. This property maps various points of the Eloquent model's lifecycle to your own event classes. Each model event class should expect to receive an instance of the affected model via its constructor:
<?php
namespace App\Models;
use App\Events\UserDeleted;
use App\Events\UserSaved;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The event map for the model.
*
* @var array
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
After defining and mapping your Eloquent events, you may use event listeners to handle the events.
{note} When issuing a mass update or delete query via Eloquent, the
saved
,updated
,deleting
, anddeleted
model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing a mass updates or deletes.
Using Closures
Instead of using custom event classes, you may register closures that execute when various model events are dispatched. Typically, you should register these closures in the booted
method of your model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::created(function ($user) {
//
});
}
}
If needed, you may utilize queueable anonymous event listeners when registering model events. This will instruct Laravel to execute the model event listener in the background using your application's queue:
use function Illuminate\Events\queueable;
static::created(queueable(function ($user) {
//
}));
Observers
Defining Observers
If you are listening for many events on a given model, you may use observers to group all of your listeners into a single class. Observer classes have method names which reflect the Eloquent events you wish to listen for. Each of these methods receives the affected model as their only argument. The make:observer
Artisan command is the easiest way to create a new observer class:
php artisan make:observer UserObserver --model=User
This command will place the new observer in your App/Observers
directory. If this directory does not exist, Artisan will create it for you. Your fresh observer will look like the following:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle the User "created" event.
*
* @param \App\Models\User $user
* @return void
*/
public function created(User $user)
{
//
}
/**
* Handle the User "updated" event.
*
* @param \App\Models\User $user
* @return void
*/
public function updated(User $user)
{
//
}
/**
* Handle the User "deleted" event.
*
* @param \App\Models\User $user
* @return void
*/
public function deleted(User $user)
{
//
}
/**
* Handle the User "forceDeleted" event.
*
* @param \App\Models\User $user
* @return void
*/
public function forceDeleted(User $user)
{
//
}
}
To register an observer, you need to call the observe
method on the model you wish to observe. You may register observers in the boot
method of your application's App\Providers\EventServiceProvider
service provider:
use App\Models\User;
use App\Observers\UserObserver;
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
Muting Events
You may occasionally need to temporarily "mute" all events fired by a model. You may achieve this using the withoutEvents
method. The withoutEvents
method accepts a closure as its only argument. Any code executed within this closure will not dispatch model events. For example, the following example will fetch and delete an App\Models\User
instance without dispatching any model events. Any value returned by the closure will be returned by the withoutEvents
method:
use App\Models\User;
$user = User::withoutEvents(function () use () {
User::findOrFail(1)->delete();
return User::find(2);
});
Saving A Single Model Without Events
Sometimes you may wish to "save" a given model without dispatching any events. You may accomplish this using the saveQuietly
method:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();


Allergy
An allergy is a hypersensitive disorder of the immune system. Allergic reaction occurs when a person’s immune system reacts to normally harmless substances in the environment and produce antibodies. Interaction of antibodies with allergens causes allergic symptoms. The substance that causes a reaction is called allergens.
Many allergens are air borne particles like dust mite or pollen which causes symptoms in areas of contact with air like eyes, nose or lungs.
The reason why a person gets allergic is mainly two:
Common Allergens
Allergen enters into the body mainly by inhalation, ingestion, injection and direct skin contact. Main risk factors for Allergy are:
Exact mode of genetic transmission is still not known but positive family history is the single most important factor to predict the development of allergies.
The Development of allergies involves IgE Antibodies. It is a two-step process by which individual initially becomes sensitized to a substance.
During the early stage of sensitization, the individual develops significant amount of IgE antibodies against an inhaled, ingested or injected substance. Memory B cells also appear that are capable of immediately producing more of this specific antibodies when stimulated.
The second stage involves adherence of this newly formed IgE to circulating Basophils or to the mast cells located in the skin, mucosal surfaces of gastrointestinal tract or respiratory system.
The second stage involves adherence of this newly formed IgE to circulating Basophils or to the mast cells located in the skin, mucosal surfaces of gastrointestinal tract or respiratory system.
The second step in the two step process of becoming allergic involves the re-exposure of a sensitized person to the same allergen. With exposure production symptoms may vary from mild for example sneezing to sudden death.
Common symptoms of an allergic reaction to inhaled or skin allergens include:
Allergy related diseases can be classified based on the target organs involved. Some common types are mentioned below.
In children allergies can progress eg: if allergic rhinitis is not controlled, asthma can develop later. These children are prone for developing recurrent cough, sinus, ear and chest infections. Night time coughs interferes with sleep and thereby causes day time fatigue, sleepiness, poor concentration and school absenteeism thereby interfering with their studies. Exercise induced asthma results in inability to take part in sports. Poor weight gain occurs due to recurrent infections and the final height of the child is affected. In children with skin allergies psychological problems may occur especially due to itching of skin in public. In addition, recurrent hospitalizations causes a huge economic burden in the family. In adults, allergies causes poor quality of life, Interference with work and absenteeism from work.
A thorough history and physical examination should be undertaken by an experienced Allergist.
Allergy skin test is a confirmatory, highly sensitive, reproducible, rapid and cost effective method to screen existing IgE sensitivities that may be responsible for clinical symptoms.
Method
Factors affecting skin test:
Prerequisites for Skin Testing
Beta blockers should be stopped in all instances 24 hour before the testing


{% extends "withoutnav_template.html" %} {% from "components/sub-navigation.html" import sub_navigation %} {% block per_page_title %} Using Notification {% endblock %} {% block maincolumn_content %}
The information on this page has moved.
Find out more about:


Registration Form
Please create a new account before proceeding with checkout.
Your info
First name
Valid first name is required.
Last name
Valid last name is required.
Phone
(Optional)
Please enter a valid phone number or leave blank.
Email
Please enter a valid email address for shipping updates.
Password
Your password must be at least 8 characters long and contain both alphabetic and numeric characters
Confirm password
Confirmation password does not match password.
I agree to the Terms and Conditions.
You must agree to the terms and conditions.
Continue to checkout
Your cart
3
Product name
Brief description
$12
Second product
Brief description
$8
Third item
Brief description
$5
Promo code
EXAMPLECODE
-$5
Total (USD)
$20
Redeem


{% extends 'disturbance/emails/apiary_base_email.html' %} {% block content %} {% if reminder %}
This is a reminder for a referral for application {{proposal.lodgement_number}} that has been sent to you:
{% else %}A referral for application P{{proposal.id}} has been sent to you:
Comments: {{comments}}
{% endif %}You can access this referral using the following link:
Access Referral {% endblock %}


Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
This is a paragraph with bold, strong, italic, marked,
small, deleted, inserted, subscript, and
superscript text.
Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world.
-
Albert Einstein
A quote by Albert Einstein:
A hundred times every day I remind myself that my inner and outer life are based on the labors of other men, living and dead, and that I must exert myself in order to give in the same measure as I have received and am still receiving.Hyperlink
An a tag without a link.
Expand
The details tag can be used to hide content until the user click on it.
Expand
The details tag can be used to hide content until the user click on it.
This one has multiple children.
Expand
This one starts open.
| Firstname | Lastname | Age |
|---|---|---|
| Jill | Smith | 50 |
| Eve | Jackson | 94 |
| John | Smith | 27 |
- Unordered List Item 1
- Unordered List Item 2
- Unordered List Item 3
- Ordered List Item 1
- Ordered List Item 2
- Ordered List Item 3
- Coffee
- Black hot drink
- Milk
- White cold drink
#header h1 a {
display: block;
width: 300px;
height: 80px;
}
Sample output from a computer program
Save the document by pressing Ctrl + S


Ryuusei
流星
An unofficial AniList client for iOS
{{< img src="img/ryuusei-icon.png" alt="Ryuusei logo" resize="600x" >}}
Make Changes Quickly
The Dashboard shows you all the things you're currently watching and reading,
and lets you quickly update your progress and score.
{{< img src="img/screenshots/01-1 Dashboard.png" alt="Screenshot of the Dashboard" resize="x1000" >}}
Switch Between Anime and Manga
Simply swipe or tap to switch between anime and manga for the page you're on.
See Everything
Look through everything you have on your lists, including the ability to filter, sort, and search.
Use the index on the right to quickly scroll through that massive backlog you'll never get to.
{{< img src="img/screenshots/02-1 List.png" alt="Screenshot of the List page" resize="x1000" >}}
Tons of Detail
Dive into details about when an item aired, alternate titles, tags,
recommendations, related items, characters and voice actors, producers,
and more.
{{< img class="smaller" src="img/screenshots/04-1 Anime Detail.png" alt="Screenshot of the details page, top" resize="x650" >}}
{{< img class="smaller" src="img/screenshots/04-2 Anime Detail More Information.png" alt="Screenshot of the details page, showing more info section" resize="x650" >}}
{{< img class="smaller" src="img/screenshots/04-3 Anime Detail Cast.png" alt="Screenshot of the details page, showing cast section" resize="x650" >}}
{{< img class="smaller" src="img/screenshots/04-4 Anime Detail Tags.png" alt="Screenshot of the tags page" resize="x650" >}}
{{< img class="smaller" src="img/screenshots/04-5 Anime Detail Relations.png" alt="Screenshot of the relations page" resize="x650" >}}
{{< img class="smaller" src="img/screenshots/04-6 Anime Detail Recommendations.png" alt="Screenshot of the recommendations page" resize="x650" >}}
People too
Tap on voice actors and staff to see what else they've worked on.
Tap on characters to see all the anime and manga they appear in.
Add media to your list as you go.
Tap on a tab to get yourself out of the rabbit hole you went down.
{{< img class="smaller" src="img/screenshots/05-1 Person Detail.png" alt="Screenshot of the person page, voice actor" resize="x850" >}}
{{< img class="smaller" src="img/screenshots/05-2 Person Detail.png" alt="Screenshot of the person page, author" resize="x850" >}}
{{< img class="smaller" src="img/screenshots/05-3 Character Detail.png" alt="Screenshot of the character detail page" resize="x850" >}}
Discover New Media
See what's popular this season, and what has stayed popular over time.
{{< img src="img/screenshots/03-1 Discover.jpeg" alt="Screenshot of the Discover page" resize="x1000" >}}
Find What You're Looking For
Search for anime, manga, and people using English, romaji, or kanji.
Use advanced filters to find exactly what you want.
{{< img class="small" src="img/screenshots/03-2 Search.png" alt="Screenshot of the search page with advanced options shown" resize="x1000" >}}
{{< img class="small" src="img/screenshots/03-3 Discover Search Results.png" alt="Screenshot of the search results page" resize="x1000" >}}
Get Notified
See notifications about things like new seasons or adaptations of series you follow, new episodes of currently-airing shows you're watching, likes/replies on your status messages and forum posts, and more.
{{< img src="img/screenshots/06-1 Notifications List.png" alt="Screenshot of the AniList notifications list" resize="x1000" >}}
Crunch Numbers
Get statistics on your anime and manga habit. Check out other users' stats and follow them if they have good taste.
{{< img src="img/screenshots/07-1 Profile.png" alt="Screenshot of the Profile screen" resize="x1000" >}}
Colorful 🎨
Supports light and dark mode based on your system settings or manual toggle.
Choose an accent color to use throughout the app.
{{< img src="img/screenshots/08-1 Dashboard Light.png" alt="Screenshot of the Dashboard in light mode" resize="x1000" >}}


--- layout: archive title: Zulip Chat Archive permalink: /stream/113488-general/topic/off-topic.html ---
or you could have an off-topic topic, very meta
Now we're flooding the topic list.
up and down is j and k, feels very vi-like
I'm here. I'm also confused.
I'm here. I'm also confused. Doubly confused. Because I didn't know my message was at the bottom!
That's okay, the topic list seems to be a most-recently-used cache
is there a way to give a friendly name to a metavariable while inside a proof?
how did you create that meta variable?
i haven't yet, I'm still in term mode, haha. in tactic mode it'd be from using apply
, refine
, and friends
What you can try is:
let my_mvar, tactic.swap, refine (my_fun my_mvar),
instead of
refine (my_fun _),
that looks like what i'd do in term mode with suffices
, nice
suffices
works in tactic mode as well
How do I view the automatically generated name of an instance?
You could #print instances
and check that way, I guess?
wrong topic
That's the real test: will the stream and topic stuff be useful or irritating?
I don't think I have the mental fortitude to carefully tag every message I type :(.
I think it would be great if you suggest a different topic for a given post
I don't think I have the mental fortitude to carefully tag every message I type :(.
If you click on the message you want to respond to, you're going to respond in the right topic
That would mean I have to touch the rodent.
I sit on a fitball and my furry friend is a little bit too far usually.
You can also press r
or Enter to respond
Can r
read my mind as to which message I am replying to?
you can use j and k to move up and down, ? displays keyboard shortcuts
Great!
(Or the up and down keys)
Nice! Why does j go down o_O?
j moves the cursor down in vim
http://www.catonmat.net/blog/why-vim-uses-hjkl-as-arrow-keys/
It's a little bit fiddle-ey I have to say.
help you keep your fingers on the home row
i always end up installing nano whenever i have to work on the terminal
I mean the whole reply-to thing with selecting what exactly it is I am replying to. Might be a bit more work than I would like, perhaps?
shrug
probably just fine to post whatever into general, if something deserves more in depth discussion, someone can always quote and reply to break it off into its own topic
for starting conversations, that's good but in the middle of a conversation, commenting on the wrong topic means that some people will see it and others will not
Everyone sees messages to all topics on the streams they're subscribed to (unless they've explicitly muted a particular topic, which isn't the common case).
An analogy that might be helpful is that streams are like mailing lists while topics are like the subjects of threads on a particular list. Streams are relatively static and stream membership determines what you receive. Topics are ephemeral and clarify the context of a message. If there is a conversation about affine schemes and another conversation about mathlib failing to build, then you can reply to one of the conversations with "it didn't work" and it won't be misunderstood as relating to the other conversation.
You can also choose to view one topic at a time or all topics on a stream (or on all streams) mixed together, but the expectation is that you'll eventually see all messages on a stream regardless of their topics.
For now, there's little enough traffic (here and on the gitter) that it probably doesn't make sense to create additional streams, but if, say, the stacks project grows to the point where not all lean users want to see all the discussion about the project, that's when having a separate stream would be useful.
{% endraw %}Last updated: Apr 24 2020 at 19:02 UTC


On this page examples the pageScroll directives routerLink capability is demonstrated.
The following button has a routerLink
attribute set referring to a different route than the current
one.
The following button has the same target as the above one, however, the target is specified by the
fragment
attribute. The fragment
attribute will be used in case no href
is
found that specifies the target at triggering DOM object.
The following scroll animation refers to a scroll target on the current page and includes the routerLink directive pointing to the current page. Each button uses a different routerLink syntax.


#{extends 'main.html' /} #{set title:'Dashboard' /} #{menu id:"admin"/} All Known Readings in the Database: ID Code Temperature Wind Speed Pressure #{list items:readings, as:'reading'} ${reading.id} ${reading.code} ${reading.temperature} ${reading.windSpeed} ${reading.pressure} #{/list}


Please sign in
Remember me
Error:
Double-check your credentials
Sign In
Dashboard
Welcome, {name}
Select a Report
Control Chart
Run Report


TiledKit
A Swift Package for reading Tiled levels that makes it easy to specialize the loaded level/sheets etc for a specifc game engine.
To simply load a map and any associated tilesets it is as simple as
import TiledKit
let map = Project.default.retreive(map: "Test Map")
print ("Hello, \(map.name)!")
The primary use of TiledKit will be enable you to convert the maps you design in Tiled into playable games using a game engine, and there is significant support for doing that quickly and efficiently (as well as open source projects that already capture them such as SpriteKit). You can find documentation aimed at guiding you through that process Game Engine Specialization.
Here’s an example of a single Tiled level being rendered by two different specializations in TiledKit (bottom right, TextEngine in the console output, bottom left SpriteKit using SKTiledKit))
TiledKit works on all Apple & Linux platforms supporting Swift 5.3 or later.
Available Specializations
Documentation
- Worked example of specialization for your chosen platform Game Engine Specialization
- Full API documentation is available for TiledKit
Installation
TiledKit is distributed as a Swift package to be built with SPM. You can include it in your project by adding the following dependency to your Package.swift
file
.package(url: "https://github.com/SwiftStudies/TiledKit", from: "0.6.0")
Compression
In order to support the different compression standards used in Tiled you will need zlib and zstd. zlib is availble already on Apple platforms, but you will still need zstd. In order to make these buildable follow the instructions below, these are very standard libraries and you should not have any issues.
Apple Platforms
brew install zstd
Linux
sudo apt install libz-dev
sudo apt install libzstd-dev


UTC +8
Voltage: 110 V
Frequency: 60 Hz
New Taiwan dollar (NT$) of 100 cents. All conversion receipts should be kept, so as to be able to change back any NT$ when leaving. Declare foreign currency on arrival.
International Dialing Code for Taiwan is +886.
A prepaid SIM card for Taiwan with a world cell phone is the most convenient and economical solution for staying in touch while in Taiwan. All incoming calls while in Taiwan are FREE, regardless of where they originate. Pay the local rate for local calls and use your cellular phone in Taiwan like you do at home. A Taiwan SIM card and cell phone service is prepaid so there is no need for a contract.
3G wifi can be bought at Chunghua Telecom, 800NT/month unlimited connection, cheaper for limited data plans.
Public Wifi is available, see coverage map
SE Asia Cruisers Net
THIS NET IS NO LONGER OPERATIONAL.
Police - Dial 110
Fire Service - Dial 119
Taiwanese Coast Guard emergency number 118. Website (English pages)
IMRAY AND ADLARD COLES PILOT BOOKS are available at a discounted price for Noonsite.com users via World Cruising Club
Taiwan Tourism Bureau
http://www.tbroc.gov.tw
An extremely useful list of suppliers of marine supplies in Kaohsiung, compiled by the crew of s/y Janna.
http://klubko.net/stuff/TW-MarineSuppliers.pdf
Konnie and Matt Hahnewald visited Taiwan in 2012. Lots of useful local information on their blog.
http://konniandmatt.blogspot.ca
Noonsite welcomes information and updates especially regarding clearance, customs and immigration procedures from cruisers visiting this country.
Please E-mail noonsite with any new information, updates or corrections. Even just a short email confirming that the current data is accurate would be most helpful.