|  Download <h1 align="center"><img src="https://i.imgur.com/fHMqwTF.png" width="170" alt="Corcel PHP"></h1> A collection of Model classes that allows you to get data directly from a WordPress database.         
 Corcel is a collection of PHP classes built on top of Eloquent ORM (from Laravel framework), that provides a fluent interface to connect and get data directly from a WordPress database. You can use WordPress as the backend (administration panel) or CMS, for inserting posts, custom types, etc, and any other PHP app in the other side querying those data (as a Model layer). It's easier to use Corcel with Laravel, but you're free to use it with any PHP project that uses Composer. <a href="https://ko-fi.com/A36513JF" target="_blank">Buy me a Coffee</a> | 
<a href="https://twitter.com/corcelphp" target="_blank">Follow Corcel on Twitter</a> Table of Contents<a id="install"></a> Installing CorcelVersion Compatibility Laravel  | Corcel
:---------|:----------
 5.1.x    | ~2.1.05.2.x    |~2.2.05.3.x    |~2.3.05.4.x    |~2.4.05.5.x    |~2.5.05.6.x    |~2.6.05.7.x    |~2.7.05.8.x    |~2.8.06.0.x    |^3.0.07.0.x    |^4.0.08.0.x    |^5.0.09.0.x    |^6.0.010.0.x   |^7.0.011.0.x   |^8.0.012.0.x   |^9.0.0 <a id="installing-corcel"></a> Installing CorcelYou need to use Composer to install Corcel into your project: composer require jgrossi/corcel
 Configuring (Laravel)<a name="config-auto-discovery"></a> Laravel 5.5 and newerCorcel wil register itself using Laravel's Auto Discovery. <a name="config-service-loader"></a> Laravel 5.4 and olderYou'll have to include CorcelServiceProviderin yourconfig/app.php: 'providers' => [
    /*
     * Package Service Providers...
     */
    Corcel\Laravel\CorcelServiceProvider::class,
]
 <a name="config-publish"></a> Publishing the configuration fileNow configure our config file to make sure your database is set correctly and to allow you to register custom post types and shortcodes in a very easy way: Run the following Artisan command in your terminal: php artisan vendor:publish --provider="Corcel\Laravel\CorcelServiceProvider"
 Now you have a config/corcel.phpconfig file, where you can set the database connection with WordPress tables and much more. <a id="database-setup"></a> Database SetupLaravel SetupJust set the database connectionyou want to be used by Corcel inconfig/corcel.php. Let' suppose you have those following database connections in your config/database.phpfile: // File: /config/database.php
'connections' => [
    'mysql' => [ // for Laravel database
        'driver'    => 'mysql',
        'host'      => 'localhost',
        'database'  => 'mydatabase',
        'username'  => 'admin'
        'password'  => 'secret',
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci',
        'prefix'    => '',
        'strict'    => false,
        'engine'    => null,
    ],
    'wordpress' => [ // for WordPress database (used by Corcel)
        'driver'    => 'mysql',
        'host'      => 'localhost',
        'database'  => 'mydatabase',
        'username'  => 'admin',
        'password'  => 'secret',
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci',
        'prefix'    => 'wp_',
        'strict'    => false,
        'engine'    => null,
    ],
],
 In this case you should want to use the wordpressconnection for Corcel, so just set it into the Corcel config fileconfig/corcel.php: 'connection' => 'wordpress',
 Other PHP Framework (not Laravel) SetupHere you have to configure the database to fit the Corcel requirements. First, you should include the Composer autoloadfile if not already loaded: require __DIR__ . '/vendor/autoload.php';
 Now you must set your WordPress database params: $params = [
    'database'  => 'database_name',
    'username'  => 'username',
    'password'  => 'pa$$word',
    'prefix'    => 'wp_' // default prefix is 'wp_', you can change to your own prefix
];
Corcel\Database::connect($params);
 You can specify all Eloquent params, but some are default (but you can override them). 'driver'    => 'mysql',
'host'      => 'localhost',
'charset'   => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix'    => 'wp_', // Specify the prefix for WordPress tables, default prefix is 'wp_'
 <a id="usage"></a>  Usage<a id="posts"></a> Posts> Every time you see Post::method(), if you're using your own Post class (where you set the connection name), likeApp\Postyou should useApp\Post::method()and notPost::method(). All the examples are assuming you already know this difference. > In the examples, every time you see Post::method()assumeCorcel\Model\Post::method(). // All published posts
$posts = Post::published()->get();
$posts = Post::status('publish')->get();
// A specific post
$post = Post::find(31);
echo $post->post_title;
 Creating your own model classesOptionally you can create your own Postmodel (or Page, or whatever) which extendsCorcel\Post. Then set the connection name (if you want to override the Corcel's default one) you're using, in this casefoo-bar: > Extending Corcel\Model\Postclass can add flexibility to your project, once you can add custom methods and logic, according what you need to use from your WordPress database. <?php // File: app/Post.php
namespace App;
use Corcel\Model\Post as Corcel;
class Post extends Corcel
{
    protected $connection = 'foo-bar';
    public function customMethod() {
        //
    }
}
 So, now you can fetch WP database data using your own class: $posts = App\Post::all(); // using the 'foo-bar' connection
 > Just remember you don't have to extends our Postclass, you can useCorcel\Model\Postand all others model without any problem. Meta Data (Custom Fields)> NOTE: In Corcel v1 you could save meta data using the Post::save()method. That's not allowed anymore. UsesaveMeta()orcreateMeta()(see below) methods to save post meta. You can retrieve meta data from posts too. // Get a custom meta value (like 'link' or whatever) from a post (any type)
$post = Post::find(31);
echo $post->meta->link; // OR
echo $post->fields->link;
echo $post->link; // OR
 To create or update meta data form a User just use the saveMeta()orsaveField()methods. They returnboollike the Eloquentsave()method. $post = Post::find(1);
$post->saveMeta('username', 'jgrossi');
 You can save many meta data at the same time too: $post = Post::find(1);
$post->saveMeta([
    'username' => 'jgrossi',
    'url' => 'http://jgrossi.com',
]);
 You also have the createMeta()andcreateField()methods, that work like thesaveX()methods, but they are used only for creation and return thePostMetacreated instance, instead ofbool. $post = Post::find(1);
$postMeta = $post->createMeta('foo', 'bar'); // instance of PostMeta class
$trueOrFalse = $post->saveMeta('foo', 'baz'); // boolean
 Querying Posts by Custom Fields (Meta)There are multiples possibilities to query posts by their custom fields (meta) by using scopes on a Post(or another other model which uses theHasMetaFieldstrait) class: To check if a meta key exists, use the hasMeta()scope: // Finds a published post with a meta flag.
$post = Post::published()->hasMeta('featured_article')->first();
 If you want to precisely match a meta-field, you can use the hasMeta()scope with a value. // Find a published post which matches both meta_key and meta_value.
$post = Post::published()->hasMeta('username', 'jgrossi')->first();
 If you need to match multiple meta-fields, you can also use the hasMeta()scope passing an array as parameter: $post = Post::hasMeta(['username' => 'jgrossi'])->first();
$post = Post::hasMeta(['username' => 'jgrossi', 'url' => 'jgrossi.com'])->first();
// Or just passing the keys
$post = Post::hasMeta(['username', 'url'])->first();
 If you need to match a case-insensitive string, or match with wildcards, you can use the hasMetaLike()scope with a value. This uses an SQLLIKEoperator, so use '%' as a wildcard operator. // Will match: 'J Grossi', 'J GROSSI', and 'j grossi'.
$post = Post::published()->hasMetaLike('author', 'J GROSSI')->first();
// Using % as a wildcard will match: 'J Grossi', 'J GROSSI', 'j grossi', 'Junior Grossi' etc.
$post = Post::published()->hasMetaLike('author', 'J%GROSSI')->first();
 Fields AliasesThe Postclass has support to "aliases", so if you check thePostclass you should note some aliases defined in the static$aliasesarray, liketitleforpost_titleandcontentforpost_content. $post = Post::find(1);
$post->title === $post->post_title; // true
 If you're extending the Postclass to create your own class you can use$aliasestoo. Just add new aliases to that static property inside your own class and it will automatically inherit all aliases from parentPostclass: class A extends \Corcel\Post
{
    protected static $aliases = [
        'foo' => 'post_foo',
    ];
}
$a = A::find(1);
echo $a->foo;
echo $a->title; // from Post class
 Custom ScopesTo order posts you can use newest()andoldest()scopes, for bothPostandUserclasses: $newest = Post::newest()->first();
$oldest = Post::oldest()->first();
 PaginationTo order posts just use Eloquent paginate()method: $posts = Post::published()->paginate(5);
foreach ($posts as $post) {
    // ...
}
 To display the pagination links just call the links()method:  {{ $posts->links() }}
 <a id="acf"></a>  Advanced Custom Fields (ACF)If you want to retrieve a custom field created by the Advanced Custom Fields (ACF) plugin, you have to install the corcel/acfplugin - click here for more information - and call the custom field like this: $post = Post::find(123);
echo $post->acf->some_radio_field;
$repeaterFields = $post->acf->my_repeater_name;
 To avoid unnecessary SQL queries just set the field type you're requesting. Usually two SQL queries are necessary to get the field type, so if you want to specify it you're skipping those extra queries: $post = Post::find(123);
echo $post->acf->text('text_field_name');
echo $post->acf->boolean('boolean_field_name');
 <a id="custom-post"></a> Custom Post TypeYou can work with custom post types too. You can use the type(string)method or create your own class. // using type() method
$videos = Post::type('video')->status('publish')->get();
// using your own class
class Video extends Corcel\Post
{
    protected $postType = 'video';
}
$videos = Video::status('publish')->get();
 Using type()method will make Corcel to return all objects asCorcel\Post. Using your custom class you have the advantage to customize classes, including custom methods and properties, return all objects asVideo, for example. Custom post types and meta data: // Get 3 posts with custom post type (store) and show its address
$stores = Post::type('store')->status('publish')->take(3)->get();
foreach ($stores as $store) {
    $storeAddress = $store->address; // option 1
    $storeAddress = $store->meta->address; // option 2
    $storeAddress = $store->fields->address; // option 3
}
 Configuring the returning InstanceEvery time you call something like Post::type('video)->first()orVideo::first()you receive aCorcel\Model\Postinstance. If you choose to create a new class for your custom post type, you can have this class be returned for all instances of that post type. Registering Post Types (the easy way)Instead of call Post::registerPostType()method for all custom post type you want to register, just use the Corcel's config file and map all custom posts and it's classes. They will be registered automatically for you: 'post_types' => [
    'video' => App\Video::class,
    'foo' => App\Foo::class,
]
 So every time you query a custom post type the mapped instance will be returned. > This is particular useful when you are intending to get a Collection of Posts of different types (e.g. when fetching the posts defined in a menu). Registering Post Types (the hard way)//all objects in the $videos Collection will be instances of Post
$videos = Post::type('video')->status('publish')->get();
// register the video custom post type and its particular class
Post::registerPostType('video', '\App\Video')
//now all objects in the $videos Collection will be instances of Video
$videos = Post::type('video')->status('publish')->get();
 You can also do this for inbuilt classes, such as Page or Post. Simply register the Page or Post class with the associated post type string, and that object will be returned instead of the default one. <a id="shortcodes"></a> ShortcodesFrom config (Laravel)You can map all shortcodes you want inside the config/corcel.phpfile, under the'shortcodes'key. In this case you should create your own class thatimplementstheCorcel\Shortcodeinterface, that requires arender()method: 'shortcodes' => [
    'foo' => App\Shortcodes\FooShortcode::class,
    'bar' => App\Shortcodes\BarShortcode::class,
],
 This is a sample shortcode class: class FakeShortcode implements \Corcel\Shortcode
{
    /
     * @param ShortcodeInterface $shortcode
     * @return string
     */
    public function render(ShortcodeInterface $shortcode)
    {
        return sprintf(
            'html-for-shortcode-%s-%s',
            $shortcode->getName(),
            $shortcode->getParameter('one')
        );
    }
}
 In runtimeYou can add shortcodes by calling the addShortcodemethod on thePostmodel : // [gallery id="1"]
Post::addShortcode('gallery', function ($shortcode) {
    return $shortcode->getName() . '.' . $shortcode->getParameter('id');
});
$post = Post::find(1);
echo $post->content;
 > Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider If you are using Laravel, we suggest adding your shortcodes handlers in App\Providers\AppServiceProvider, in thebootmethod. Shortcode ParsingShortcodes are parsed with the thunderer/shortcode library.  Several different parsers are provided. RegularParseris the most technically correct and is provided by default. This is suitable for most cases. However if you encounter some irregularities in your shortcode parsing, you may need to configure Corcel to use theWordpressParser, which more faithfully matches WordPress' shortcode regex. To do this, if you are using Laravel, edit theconfig/corcel.phpfile, and uncomment your preferred parser. Alternatively, you can replace this with a parser of your own. 'shortcode_parser' => Thunder\Shortcode\Parser\RegularParser::class,
// 'shortcode_parser' => Thunder\Shortcode\Parser\WordpressParser::class,
 If you are not using Laravel, you can to do this in runtime, calling the setShortcodeParser()method from any class which uses theShortcodestrait, such asPost, for example. $post->setShortcodeParser(new WordpressParser());
echo $post->content; // content parsed with "WordpressParser" class
 For more information about the shortcode package, click here. <a id="taxonomies"></a>TaxonomiesYou can get taxonomies for a specific post like: $post = Post::find(1);
$taxonomy = $post->taxonomies()->first();
echo $taxonomy->taxonomy;
 Or you can search for posts using its taxonomies: $post = Post::taxonomy('category', 'php')->first();
 <a id="post-format"></a>Post FormatYou can also get the post format, like the WordPress function get_post_format(): echo $post->getFormat(); // should return something like 'video', etc
 <a id="pages"></a>PagesPages are like custom post types. You can use Post::type('page')or theCorcel\Model\Pageclass. 
use Corcel\Model\Page;
// Find a page by slug
$page = Page::slug('about')->first(); // OR
$page = Post::type('page')->slug('about')->first();
echo $page->post_title;
 <a id="cats"></a>Categories and TaxonomiesGet a category or taxonomy or load posts from a certain category. There are multiple ways
to achieve it. // all categories
$cat = Taxonomy::category()->slug('uncategorized')->posts->first();
echo "<pre>"; print_r($cat->name); echo "</pre>";
// only all categories and posts connected with it
$cat = Taxonomy::where('taxonomy', 'category')->with('posts')->get();
$cat->each(function($category) {
    echo $category->name;
});
// clean and simple all posts from a category
$cat = Category::slug('uncategorized')->posts->first();
$cat->posts->each(function($post) {
    echo $post->post_title;
});
 <a id="attachments"></a>Attachment and RevisionGetting the attachment and/or revision from a PostorPage. $page = Page::slug('about')->with('attachment')->first();
// get feature image from page or post
print_r($page->attachment);
$post = Post::slug('test')->with('revision')->first();
// get all revisions from a post or page
print_r($post->revision);
 <a id="thumbnails"></a>ThumbnailsGetting the thumbnail for a PostorPage. $post = Post::find(1);
// Retrieve an instance of Corcel\Model\Meta\ThumbnailMeta.
print_r($post->thumbnail);
// For convenience you may also echo the thumbnail instance to get the URL of the original image.
echo $post->thumbnail;
 To retrieve a particular thumbnail size you may call the ->size()method on the thumbnail object and pass in a thumbnail size string parameter (e.g.thumbnailormedium). If the thumbnail has been generated, this method returns an array of image metadata, otherwise the original image URL will be returned as a fallback. if ($post->thumbnail !== null) {
    /
     * [
     *     'file' => 'filename-300x300.jpg',
     *     'width' => 300,
     *     'height' => 300,
     *     'mime-type' => 'image/jpeg',
     *     'url' => 'http://localhost/wp-content/uploads/filename-300x300.jpg',
     * ]
     */
    print_r($post->thumbnail->size(Corcel\Model\Meta\ThumbnailMeta::SIZE_THUMBNAIL));
    // http://localhost/wp-content/uploads/filename.jpg
    print_r($post->thumbnail->size('invalid_size'));
}
 <a id="options"></a>Options> In previous versions of Corcel this classe was called Optionsinstead ofOption(singular). So take care of using always this class in the singular form starting fromv2.0.0. > The Option::getAll()method was removed in Corcel 2+, in favor ofOption::asArray($keys []). You can use the Optionclass to get data fromwp_optionstable: $siteUrl = Option::get('siteurl');
 You can also add new options: Option::add('foo', 'bar'); // stored as string
Option::add('baz', ['one' => 'two']); // this will be serialized and saved
 You can get all options in a simple array: $options = Option::asArray();
echo $options['siteurl'];
 Or you can specify only the keys you want to get: $options = Option::asArray(['siteurl', 'home', 'blogname']);
echo $options['home'];
 <a id="menu"></a> MenuTo get a menu by its slug, use the syntax below. The menu items will be loaded in the itemsvariable (it's a collection ofCorcel\Model\MenuItemobjects). The currently supported menu items are: Pages, Posts, Custom Links and Categories. Once you'll have instances of MenuItemclass, if you want to use the original instance (like the original Page or Term, for example), just call theMenuItem::instance()method. TheMenuItemobject is just a post withpost_typeequalsnav_menu_item: $menu = Menu::slug('primary')->first();
foreach ($menu->items as $item) {
    echo $item->instance()->title; // if it's a Post
    echo $item->instance()->name; // if it's a Term
    echo $item->instance()->link_text; // if it's a custom link
}
 The instance()method will return the matching object: 
`Post` instance for `post` menu item;
`Page` instance for `page` menu item;
`CustomLink` instance for `custom` menu item;
`Term` instance for `category` menu item.
 Multi-levels MenusTo handle multi-levels menus, loop through all the menu items to put them on the right levels, for example. You can use the MenuItem::parent()method to retrieve the parent instance of that menu item: $items = Menu::slug('foo')->first()->items;
$parent = $items->first()->parent(); // Post, Page, CustomLink or Term (category)
 To group menu items according their parents, you can use the ->groupBy()method in the$menu->itemscollection, grouping menu items by their$item->parent()->ID. To read more about the groupBy()method take a look on the Laravel documentation. <a id="users"></a> UsersYou can manipulate users in the same manner you work with posts: // All users
$users = User::get();
// A specific user
$user = User::find(1);
echo $user->user_login;
 <a id="auth"></a>AuthenticationUsing LaravelIf you're using Laravel 5.4 or older, make sure you have the CorcelServiceProviderprovider registered. And then, define the user provider in config/auth.phpto allow Laravel to login with WordPress users: 'providers' => [
    'users' => [
        'driver' => 'corcel',
        'model'  => Corcel\Model\User::class,
    ],
],
 Now you can use the Authfacade to authenticate users: Auth::validate([
    'email' => '[email protected]', // or using 'username' too
    'password' => 'secret',
]);
 To make Laravel's Password Reset work with Corcel, we have to override how passwords are stored in the database. To do this, you must change Auth/PasswordController.phpfrom: use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
    use ResetsPasswords;
 to use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Corcel\Laravel\Auth\ResetsPasswords as CorcelResetsPasswords;
class PasswordController extends Controller
{
    use ResetsPasswords, CorcelResetsPasswords {
        CorcelResetsPasswords::resetPassword insteadof ResetsPasswords;
    }
 Not using LaravelYou can use the AuthUserProviderclass to manually authenticate a user : $userProvider = new Corcel\Laravel\Auth\AuthUserProvider;
$user = $userProvider->retrieveByCredentials(['username' => 'admin']);
if(!is_null($user) && $userProvider->validateCredentials($user, ['password' => 'admin'])) {
    // successfully login
}
 > Remember you can use both usernameandemailas credentials for a User. <a id="tests"></a> Running TestsTo run the phpunit tests, execute the following command : ./vendor/bin/phpunit
 If you have the global phpunitcommand installed you can just type: phpunit
 All tests were written using Sqlite with :memorydatabase, so it runs in your memory. All tests usefactoriesandmigrations. Take a look ontests/database/factoriesandtests/database/migrationsdirectories for more information. <a id="contrib"></a> ContributingAll contributions are welcome to help improve Corcel. Before you submit your Pull Request (PR) consider the following guidelines: 
Fork https://github.com/corcel/corcel in Github;
Clone your forked repository (not Corcel's) locally and create your own branch based on the version you want to fix (`2.1`, `2.2`, `2.3`, `2.4` or `2.5`): `git checkout -b my-fix-branch 2.5`;
Make all code changes. Remember here to write at least one test case for any feature you add or any bugfix (if it's not tested yet). Our goal is to have 100% of the code covered by tests, so help us to write a better code ;-) If you don' have experience with tests it's a good opportunity to learn. Just take a look into our tests cases and you'll see how simple they are.
Run the unit tests locally to make sure your changes did not break any other piece of code;
Push your new branch to your forked repository, usually `git push origin HEAD` should work;
In GitHub again, create a Pull Request (PR) from your custom `my-fix-branch` branch (from your forked repository) to the related branch (`corcel:2.5`, for example, not `corcel:master`, please;
Wait for the approval :-)
 <a id="license"></a> LicenceMIT License © Junior Grossi |