{"id":13453411,"url":"https://github.com/corcel/corcel","last_synced_at":"2025-12-18T14:06:49.439Z","repository":{"id":8982815,"uuid":"10728912","full_name":"corcel/corcel","owner":"corcel","description":"Use WordPress backend with Laravel or any PHP application","archived":false,"fork":false,"pushed_at":"2025-03-19T14:09:00.000Z","size":1675,"stargazers_count":4778,"open_issues_count":108,"forks_count":603,"subscribers_count":152,"default_branch":"9.0","last_synced_at":"2025-12-10T22:59:42.648Z","etag":null,"topics":["corcel","eloquent","hacktoberfest","laravel","mvc","php","shortcode","taxonomies","wordpress"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/corcel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-06-17T02:43:12.000Z","updated_at":"2025-12-05T15:17:37.000Z","dependencies_parsed_at":"2024-01-07T01:18:28.203Z","dependency_job_id":"3b4f5327-5349-4506-9317-c727dfb329da","html_url":"https://github.com/corcel/corcel","commit_stats":{"total_commits":743,"total_committers":71,"mean_commits":"10.464788732394366","dds":"0.36204576043068637","last_synced_commit":"b269f0bf932d2ef26cc998e4bc498c0b9ad01a5f"},"previous_names":["jgrossi/corcel"],"tags_count":83,"template":false,"template_full_name":null,"purl":"pkg:github/corcel/corcel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corcel%2Fcorcel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corcel%2Fcorcel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corcel%2Fcorcel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corcel%2Fcorcel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/corcel","download_url":"https://codeload.github.com/corcel/corcel/tar.gz/refs/heads/9.0","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corcel%2Fcorcel/sbom","scorecard":{"id":304623,"data":{"date":"2025-08-11","repo":{"name":"github.com/corcel/corcel","commit":"83506aa195ddaa4a404ac12a56dbc51b906505fc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":2,"reason":"Found 4/17 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/corcel/corcel/ci.yml/9.0?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/corcel/corcel/ci.yml/9.0?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/corcel/corcel/ci.yml/9.0?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/corcel/corcel/ci.yml/9.0?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during GetBranch(3.0): error during branchesHandler.query: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-3527-qv2q-pfvx"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T21:43:17.350Z","repository_id":8982815,"created_at":"2025-08-17T21:43:17.350Z","updated_at":"2025-08-17T21:43:17.350Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27797824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-18T02:00:09.725Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["corcel","eloquent","hacktoberfest","laravel","mvc","php","shortcode","taxonomies","wordpress"],"created_at":"2024-07-31T08:00:39.594Z","updated_at":"2025-12-18T14:06:49.411Z","avatar_url":"https://github.com/corcel.png","language":"PHP","readme":"\u003ch1 align=\"center\"\u003e\u003cimg src=\"https://i.imgur.com/fHMqwTF.png\" width=\"170\" alt=\"Corcel PHP\"\u003e\u003c/h1\u003e\n\n**A collection of Model classes that allows you to get data directly from a WordPress database.**\n\n[![Actions Status](https://github.com/corcel/corcel/workflows/CI/badge.svg)](https://github.com/corcel/corcel/actions)\n[![Packagist](https://img.shields.io/packagist/v/jgrossi/corcel.svg)](https://packagist.org/packages/jgrossi/corcel)\n[![Packagist](https://img.shields.io/packagist/dt/jgrossi/corcel.svg)](https://github.com/jgrossi/corcel/releases)\n[![Test Coverage](https://codeclimate.com/github/corcel/corcel/badges/coverage.svg)](https://codeclimate.com/github/corcel/corcel/coverage)\n[![Maintainability](https://api.codeclimate.com/v1/badges/3dc8135eee70ae7da325/maintainability)](https://codeclimate.com/github/corcel/corcel/maintainability)\n\nCorcel is a collection of PHP classes built on top of [Eloquent ORM](https://laravel.com/docs/master/eloquent) (from [Laravel](http://laravel.com) framework), that provides a fluent interface to connect and get data directly from a [WordPress](http://wordpress.org) database.\n\nYou 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.\n\n\u003ca href=\"https://ko-fi.com/A36513JF\" target=\"_blank\"\u003eBuy me a Coffee\u003c/a\u003e | \n\u003ca href=\"https://twitter.com/corcelphp\" target=\"_blank\"\u003eFollow Corcel on Twitter\u003c/a\u003e\n\n# Table of Contents\n# \u003ca id=\"install\"\u003e\u003c/a\u003e Installing Corcel\n\n\n- [Version Compatibility](#version-compatibility)\n- [Installing Corcel](#installing-corcel)\n- [Database Setup](#database-setup)\n- [Usage](#usage)\n    - [Posts](#posts)\n    - [Advanced Custom Fields (ACF) Integration](#acf)\n    - [Custom Post Type](#custom-post)\n    - [Single Table Inheritance](#single-tab)\n    - [Taxonomies](#taxonomies)\n    - [Post Format](#post-format)\n    - [Pages](#pages)\n    - [Categories \u0026 Taxonomies](#cats)\n    - [Attachments \u0026 Revision](#attachments)\n    - [Thumbnails](#thumbnails)\n    - [Options](#options)\n    - [Menu](#menu)\n    - [Users](#users)\n    - [Authentication](#auth)\n    - [Running Tests](#tests)\n- [Contributing](#contrib)\n- [License](#license)\n\n# Version Compatibility\n\n Laravel  | Corcel\n:---------|:----------\n 5.1.x    | `~2.1.0`\n 5.2.x    | `~2.2.0`\n 5.3.x    | `~2.3.0`\n 5.4.x    | `~2.4.0`\n 5.5.x    | `~2.5.0`\n 5.6.x    | `~2.6.0`\n 5.7.x    | `~2.7.0`\n 5.8.x    | `~2.8.0`\n 6.0.x    | `^3.0.0`\n 7.0.x    | `^4.0.0`\n 8.0.x    | `^5.0.0`\n 9.0.x    | `^6.0.0`\n 10.0.x   | `^7.0.0`\n 11.0.x   | `^8.0.0`\n 12.0.x   | `^9.0.0`\n\n# \u003ca id=\"installing-corcel\"\u003e\u003c/a\u003e Installing Corcel\n\nYou need to use Composer to install Corcel into your project:\n\n```\ncomposer require jgrossi/corcel\n```\n\n## Configuring (Laravel)\n\n### \u003ca name=\"config-auto-discovery\"\u003e\u003c/a\u003e Laravel 5.5 and newer\n\nCorcel wil register itself using Laravel's [Auto Discovery](https://laravel.com/docs/5.5/packages#package-discovery).\n\n### \u003ca name=\"config-service-loader\"\u003e\u003c/a\u003e Laravel 5.4 and older\n\nYou'll have to include `CorcelServiceProvider` in your `config/app.php`:\n\n```php\n'providers' =\u003e [\n    /*\n     * Package Service Providers...\n     */\n    Corcel\\Laravel\\CorcelServiceProvider::class,\n]\n```\n\n### \u003ca name=\"config-publish\"\u003e\u003c/a\u003e Publishing the configuration file\n\nNow 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:\n\nRun the following Artisan command in your terminal:\n\n```\nphp artisan vendor:publish --provider=\"Corcel\\Laravel\\CorcelServiceProvider\"\n```\n\nNow you have a `config/corcel.php` config file, where you can set the database connection with WordPress tables and much more.\n\n# \u003ca id=\"database-setup\"\u003e\u003c/a\u003e Database Setup\n\n## Laravel Setup\n\nJust set the database `connection` you want to be used by Corcel in `config/corcel.php`.\n\nLet' suppose you have those following database connections in your `config/database.php` file:\n\n```php\n// File: /config/database.php\n\n'connections' =\u003e [\n\n    'mysql' =\u003e [ // for Laravel database\n        'driver'    =\u003e 'mysql',\n        'host'      =\u003e 'localhost',\n        'database'  =\u003e 'mydatabase',\n        'username'  =\u003e 'admin'\n        'password'  =\u003e 'secret',\n        'charset'   =\u003e 'utf8',\n        'collation' =\u003e 'utf8_unicode_ci',\n        'prefix'    =\u003e '',\n        'strict'    =\u003e false,\n        'engine'    =\u003e null,\n    ],\n\n    'wordpress' =\u003e [ // for WordPress database (used by Corcel)\n        'driver'    =\u003e 'mysql',\n        'host'      =\u003e 'localhost',\n        'database'  =\u003e 'mydatabase',\n        'username'  =\u003e 'admin',\n        'password'  =\u003e 'secret',\n        'charset'   =\u003e 'utf8',\n        'collation' =\u003e 'utf8_unicode_ci',\n        'prefix'    =\u003e 'wp_',\n        'strict'    =\u003e false,\n        'engine'    =\u003e null,\n    ],\n],\n```\n\nIn this case you should want to use the `wordpress` connection for Corcel, so just set it into the Corcel config file `config/corcel.php`:\n\n```php\n'connection' =\u003e 'wordpress',\n```\n\n## Other PHP Framework (not Laravel) Setup\n\nHere you have to configure the database to fit the Corcel requirements. First, you should include the Composer `autoload` file if not already loaded:\n\n```php\nrequire __DIR__ . '/vendor/autoload.php';\n```\n\nNow you must set your WordPress database params:\n\n```php\n$params = [\n    'database'  =\u003e 'database_name',\n    'username'  =\u003e 'username',\n    'password'  =\u003e 'pa$$word',\n    'prefix'    =\u003e 'wp_' // default prefix is 'wp_', you can change to your own prefix\n];\nCorcel\\Database::connect($params);\n```\n\nYou can specify all Eloquent params, but some are default (but you can override them).\n\n```php\n'driver'    =\u003e 'mysql',\n'host'      =\u003e 'localhost',\n'charset'   =\u003e 'utf8',\n'collation' =\u003e 'utf8_unicode_ci',\n'prefix'    =\u003e 'wp_', // Specify the prefix for WordPress tables, default prefix is 'wp_'\n```\n\n# \u003ca id=\"usage\"\u003e\u003c/a\u003e  Usage\n\n## \u003ca id=\"posts\"\u003e\u003c/a\u003e Posts\n\n\u003e Every time you see `Post::method()`, if you're using your own Post class (where you set the connection name), like `App\\Post` you should use `App\\Post::method()` and not `Post::method()`. All the examples are assuming you already know this difference.\n\n\u003e In the examples, every time you see `Post::method()` assume `Corcel\\Model\\Post::method()`.\n\n```php\n// All published posts\n$posts = Post::published()-\u003eget();\n$posts = Post::status('publish')-\u003eget();\n\n// A specific post\n$post = Post::find(31);\necho $post-\u003epost_title;\n```\n\n## Creating your own model classes\n\nOptionally you can create your own `Post` model (or Page, or whatever) which extends `Corcel\\Post`. Then set the connection name (if you want to override the Corcel's default one) you're using, in this case `foo-bar`:\n\n\u003e Extending `Corcel\\Model\\Post` class can add flexibility to your project, once you can add custom methods and logic, according what you need to use from your WordPress database.\n\n```php\n\u003c?php // File: app/Post.php\n\nnamespace App;\n\nuse Corcel\\Model\\Post as Corcel;\n\nclass Post extends Corcel\n{\n    protected $connection = 'foo-bar';\n\n    public function customMethod() {\n        //\n    }\n}\n```\n\nSo, now you can fetch WP database data using your own class:\n\n```php\n$posts = App\\Post::all(); // using the 'foo-bar' connection\n```\n\n\u003e Just remember you don't have to extends our `Post` class, you can use `Corcel\\Model\\Post` and all others model without any problem.\n\n### Meta Data (Custom Fields)\n\n\u003e NOTE: In Corcel v1 you could save meta data using the `Post::save()` method. That's not allowed anymore. Use `saveMeta()` or `createMeta()` (see below) methods to save post meta.\n\nYou can retrieve meta data from posts too.\n\n```php\n// Get a custom meta value (like 'link' or whatever) from a post (any type)\n$post = Post::find(31);\necho $post-\u003emeta-\u003elink; // OR\necho $post-\u003efields-\u003elink;\necho $post-\u003elink; // OR\n```\n\nTo create or update meta data form a User just use the `saveMeta()` or `saveField()` methods. They return `bool` like the Eloquent `save()` method.\n\n```php\n$post = Post::find(1);\n$post-\u003esaveMeta('username', 'jgrossi');\n```\n\nYou can save many meta data at the same time too:\n\n```php\n$post = Post::find(1);\n$post-\u003esaveMeta([\n    'username' =\u003e 'jgrossi',\n    'url' =\u003e 'http://jgrossi.com',\n]);\n```\n\nYou also have the `createMeta()` and `createField()` methods, that work like the `saveX()` methods, but they are used only for creation and return the `PostMeta` created instance, instead of `bool`.\n\n```php\n$post = Post::find(1);\n$postMeta = $post-\u003ecreateMeta('foo', 'bar'); // instance of PostMeta class\n$trueOrFalse = $post-\u003esaveMeta('foo', 'baz'); // boolean\n```\n\n### Querying Posts by Custom Fields (Meta)\n\nThere are multiples possibilities to query posts by their custom fields (meta) by using scopes on a `Post` (or another other model which uses the `HasMetaFields` trait) class:\n\nTo check if a meta key exists, use the `hasMeta()` scope:\n```\n// Finds a published post with a meta flag.\n$post = Post::published()-\u003ehasMeta('featured_article')-\u003efirst();\n```\n\nIf you want to precisely match a meta-field, you can use the `hasMeta()` scope with a value.\n\n```php\n// Find a published post which matches both meta_key and meta_value.\n$post = Post::published()-\u003ehasMeta('username', 'jgrossi')-\u003efirst();\n```\n\nIf you need to match multiple meta-fields, you can also use the `hasMeta()` scope passing an array as parameter:\n\n```php\n$post = Post::hasMeta(['username' =\u003e 'jgrossi'])-\u003efirst();\n$post = Post::hasMeta(['username' =\u003e 'jgrossi', 'url' =\u003e 'jgrossi.com'])-\u003efirst();\n// Or just passing the keys\n$post = Post::hasMeta(['username', 'url'])-\u003efirst();\n```\n\nIf you need to match a case-insensitive string, or match with wildcards, you can use the `hasMetaLike()` scope with a value. This uses an SQL `LIKE` operator, so use '%' as a wildcard operator.\n\n```php\n// Will match: 'J Grossi', 'J GROSSI', and 'j grossi'.\n$post = Post::published()-\u003ehasMetaLike('author', 'J GROSSI')-\u003efirst();\n\n// Using % as a wildcard will match: 'J Grossi', 'J GROSSI', 'j grossi', 'Junior Grossi' etc.\n$post = Post::published()-\u003ehasMetaLike('author', 'J%GROSSI')-\u003efirst();\n```\n\n### Fields Aliases\n\nThe `Post` class has support to \"aliases\", so if you check the `Post` class you should note some aliases defined in the static `$aliases` array, like `title` for `post_title` and `content` for `post_content`.\n\n```php\n$post = Post::find(1);\n$post-\u003etitle === $post-\u003epost_title; // true\n```\n\nIf you're extending the `Post` class to create your own class you can use `$aliases` too. Just add new aliases to that static property inside your own class and it will automatically inherit all aliases from parent `Post` class:\n\n```php\nclass A extends \\Corcel\\Post\n{\n    protected static $aliases = [\n        'foo' =\u003e 'post_foo',\n    ];\n}\n\n$a = A::find(1);\necho $a-\u003efoo;\necho $a-\u003etitle; // from Post class\n```\n\n### Custom Scopes\n\nTo order posts you can use `newest()` and `oldest()` scopes, for both `Post` and `User` classes:\n\n```php\n$newest = Post::newest()-\u003efirst();\n$oldest = Post::oldest()-\u003efirst();\n```\n\n### Pagination\n\nTo order posts just use Eloquent `paginate()` method:\n\n```php\n$posts = Post::published()-\u003epaginate(5);\nforeach ($posts as $post) {\n    // ...\n}\n```\n\nTo display the pagination links just call the `links()` method:\n\n ```php\n {{ $posts-\u003elinks() }}\n ```\n\n## \u003ca id=\"acf\"\u003e\u003c/a\u003e  Advanced Custom Fields (ACF)\n\nIf you want to retrieve a custom field created by the [Advanced Custom Fields (ACF)](http://advancedcustomfields.com) plugin, you have to install the `corcel/acf` plugin - [click here for more information](http://github.com/corcel/acf) - and call the custom field like this:\n\n```php\n$post = Post::find(123);\necho $post-\u003eacf-\u003esome_radio_field;\n$repeaterFields = $post-\u003eacf-\u003emy_repeater_name;\n```\n\nTo 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:\n\n```php\n$post = Post::find(123);\necho $post-\u003eacf-\u003etext('text_field_name');\necho $post-\u003eacf-\u003eboolean('boolean_field_name');\n```\n\n## \u003ca id=\"custom-post\"\u003e\u003c/a\u003e Custom Post Type\n\nYou can work with custom post types too. You can use the `type(string)` method or create your own class.\n\n```php\n// using type() method\n$videos = Post::type('video')-\u003estatus('publish')-\u003eget();\n\n// using your own class\nclass Video extends Corcel\\Post\n{\n    protected $postType = 'video';\n}\n$videos = Video::status('publish')-\u003eget();\n```\n\nUsing `type()` method will make Corcel to return all objects as `Corcel\\Post`. Using your custom class you have the advantage to customize classes, including custom methods and properties, return all objects as `Video`, for example.\n\nCustom post types and meta data:\n\n```php\n// Get 3 posts with custom post type (store) and show its address\n$stores = Post::type('store')-\u003estatus('publish')-\u003etake(3)-\u003eget();\nforeach ($stores as $store) {\n    $storeAddress = $store-\u003eaddress; // option 1\n    $storeAddress = $store-\u003emeta-\u003eaddress; // option 2\n    $storeAddress = $store-\u003efields-\u003eaddress; // option 3\n}\n```\n\n### Configuring the returning Instance\n\nEvery time you call something like `Post::type('video)-\u003efirst()` or `Video::first()` you receive a `Corcel\\Model\\Post` instance.\n\nIf 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.\n\n#### Registering Post Types (the easy way)\n\nInstead 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:\n\n```php\n'post_types' =\u003e [\n    'video' =\u003e App\\Video::class,\n    'foo' =\u003e App\\Foo::class,\n]\n```\n\nSo every time you query a custom post type the mapped instance will be returned.\n\n\u003e 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).\n\n#### Registering Post Types (the hard way)\n\n```php\n//all objects in the $videos Collection will be instances of Post\n$videos = Post::type('video')-\u003estatus('publish')-\u003eget();\n\n// register the video custom post type and its particular class\nPost::registerPostType('video', '\\App\\Video')\n\n\n//now all objects in the $videos Collection will be instances of Video\n$videos = Post::type('video')-\u003estatus('publish')-\u003eget();\n```\n\nYou 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.\n\n## \u003ca id=\"shortcodes\"\u003e\u003c/a\u003e Shortcodes\n\n### From config (Laravel)\n\nYou can map all shortcodes you want inside the `config/corcel.php` file, under the `'shortcodes'` key. In this case you should create your own class that `implements` the `Corcel\\Shortcode` interface, that requires a `render()` method:\n\n```php\n'shortcodes' =\u003e [\n    'foo' =\u003e App\\Shortcodes\\FooShortcode::class,\n    'bar' =\u003e App\\Shortcodes\\BarShortcode::class,\n],\n```\n\nThis is a sample shortcode class:\n\n```php\nclass FakeShortcode implements \\Corcel\\Shortcode\n{\n    /**\n     * @param ShortcodeInterface $shortcode\n     * @return string\n     */\n    public function render(ShortcodeInterface $shortcode)\n    {\n        return sprintf(\n            'html-for-shortcode-%s-%s',\n            $shortcode-\u003egetName(),\n            $shortcode-\u003egetParameter('one')\n        );\n    }\n}\n```\n\n### In runtime\n\nYou can add [shortcodes](https://codex.wordpress.org/Shortcode_API) by calling the `addShortcode` method on the `Post` model :\n\n```php\n// [gallery id=\"1\"]\nPost::addShortcode('gallery', function ($shortcode) {\n    return $shortcode-\u003egetName() . '.' . $shortcode-\u003egetParameter('id');\n});\n$post = Post::find(1);\necho $post-\u003econtent;\n```\n\n\u003e Laravel 5.5 uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider\n\nIf you are using Laravel, we suggest adding your shortcodes handlers in `App\\Providers\\AppServiceProvider`, in the `boot` method.\n\n### Shortcode Parsing\n\nShortcodes are parsed with the [*thunderer/shortcode*](https://github.com/thunderer/Shortcode) library. \n\nSeveral different parsers are provided. `RegularParser` is 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 the `WordpressParser`, which more faithfully matches WordPress' shortcode regex. To do this, if you are using Laravel, edit the `config/corcel.php` file, and uncomment your preferred parser. Alternatively, you can replace this with a parser of your own.\n\n```php\n'shortcode_parser' =\u003e Thunder\\Shortcode\\Parser\\RegularParser::class,\n// 'shortcode_parser' =\u003e Thunder\\Shortcode\\Parser\\WordpressParser::class,\n```\n\nIf you are not using Laravel, you can to do this in runtime, calling the `setShortcodeParser()` method from any class which uses the `Shortcodes` trait, such as `Post`, for example.\n\n```php\n$post-\u003esetShortcodeParser(new WordpressParser());\necho $post-\u003econtent; // content parsed with \"WordpressParser\" class\n```\n\nFor more information about the shortcode package, [click here](https://github.com/thunderer/Shortcode).\n\n## \u003ca id=\"taxonomies\"\u003e\u003c/a\u003eTaxonomies\n\nYou can get taxonomies for a specific post like:\n\n```php\n$post = Post::find(1);\n$taxonomy = $post-\u003etaxonomies()-\u003efirst();\necho $taxonomy-\u003etaxonomy;\n```\n\nOr you can search for posts using its taxonomies:\n\n```php\n$post = Post::taxonomy('category', 'php')-\u003efirst();\n```\n\n## \u003ca id=\"post-format\"\u003e\u003c/a\u003ePost Format\n\nYou can also get the post format, like the WordPress function `get_post_format()`:\n\n```php\necho $post-\u003egetFormat(); // should return something like 'video', etc\n```\n\n## \u003ca id=\"pages\"\u003e\u003c/a\u003ePages\n\nPages are like custom post types. You can use `Post::type('page')` or the `Corcel\\Model\\Page` class.\n\n```php\n\nuse Corcel\\Model\\Page;\n\n// Find a page by slug\n$page = Page::slug('about')-\u003efirst(); // OR\n$page = Post::type('page')-\u003eslug('about')-\u003efirst();\necho $page-\u003epost_title;\n```\n\n## \u003ca id=\"cats\"\u003e\u003c/a\u003eCategories and Taxonomies\n\nGet a category or taxonomy or load posts from a certain category. There are multiple ways\nto achieve it.\n\n```php\n// all categories\n$cat = Taxonomy::category()-\u003eslug('uncategorized')-\u003eposts-\u003efirst();\necho \"\u003cpre\u003e\"; print_r($cat-\u003ename); echo \"\u003c/pre\u003e\";\n\n// only all categories and posts connected with it\n$cat = Taxonomy::where('taxonomy', 'category')-\u003ewith('posts')-\u003eget();\n$cat-\u003eeach(function($category) {\n    echo $category-\u003ename;\n});\n\n// clean and simple all posts from a category\n$cat = Category::slug('uncategorized')-\u003eposts-\u003efirst();\n$cat-\u003eposts-\u003eeach(function($post) {\n    echo $post-\u003epost_title;\n});\n```\n\n## \u003ca id=\"attachments\"\u003e\u003c/a\u003eAttachment and Revision\n\nGetting the attachment and/or revision from a `Post` or `Page`.\n\n```php\n$page = Page::slug('about')-\u003ewith('attachment')-\u003efirst();\n// get feature image from page or post\nprint_r($page-\u003eattachment);\n\n$post = Post::slug('test')-\u003ewith('revision')-\u003efirst();\n// get all revisions from a post or page\nprint_r($post-\u003erevision);\n```\n\n## \u003ca id=\"thumbnails\"\u003e\u003c/a\u003eThumbnails\n\nGetting the thumbnail for a `Post` or `Page`.\n\n```php\n$post = Post::find(1);\n\n// Retrieve an instance of Corcel\\Model\\Meta\\ThumbnailMeta.\nprint_r($post-\u003ethumbnail);\n\n// For convenience you may also echo the thumbnail instance to get the URL of the original image.\necho $post-\u003ethumbnail;\n```\n\nTo retrieve a particular thumbnail size you may call the `-\u003esize()` method on the thumbnail object and pass in a thumbnail size string parameter (e.g. `thumbnail` or `medium`). 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.\n\n```php\nif ($post-\u003ethumbnail !== null) {\n    /**\n     * [\n     *     'file' =\u003e 'filename-300x300.jpg',\n     *     'width' =\u003e 300,\n     *     'height' =\u003e 300,\n     *     'mime-type' =\u003e 'image/jpeg',\n     *     'url' =\u003e 'http://localhost/wp-content/uploads/filename-300x300.jpg',\n     * ]\n     */\n    print_r($post-\u003ethumbnail-\u003esize(Corcel\\Model\\Meta\\ThumbnailMeta::SIZE_THUMBNAIL));\n\n    // http://localhost/wp-content/uploads/filename.jpg\n    print_r($post-\u003ethumbnail-\u003esize('invalid_size'));\n}\n```\n\n## \u003ca id=\"options\"\u003e\u003c/a\u003eOptions\n\n\u003e In previous versions of Corcel this classe was called `Options` instead of `Option` (singular). So take care of using always this class in the singular form starting from `v2.0.0`.\n\n\u003e The `Option::getAll()` method was removed in Corcel 2+, in favor of `Option::asArray($keys [])`.\n\nYou can use the `Option` class to get data from `wp_options` table:\n\n```php\n$siteUrl = Option::get('siteurl');\n```\n\nYou can also add new options:\n\n```php\nOption::add('foo', 'bar'); // stored as string\nOption::add('baz', ['one' =\u003e 'two']); // this will be serialized and saved\n```\n\nYou can get all options in a simple array:\n\n```php\n$options = Option::asArray();\necho $options['siteurl'];\n```\n\nOr you can specify only the keys you want to get:\n\n```php\n$options = Option::asArray(['siteurl', 'home', 'blogname']);\necho $options['home'];\n```\n\n## \u003ca id=\"menu\"\u003e\u003c/a\u003e Menu\n\nTo get a menu by its slug, use the syntax below. The menu items will be loaded in the `items` variable (it's a collection of `Corcel\\Model\\MenuItem` objects).\n\nThe currently supported menu items are: Pages, Posts, Custom Links and Categories.\n\nOnce you'll have instances of `MenuItem` class, if you want to use the original instance (like the original Page or Term, for example), just call the `MenuItem::instance()` method. The `MenuItem` object is just a post with `post_type` equals `nav_menu_item`:\n\n```php\n$menu = Menu::slug('primary')-\u003efirst();\n\nforeach ($menu-\u003eitems as $item) {\n    echo $item-\u003einstance()-\u003etitle; // if it's a Post\n    echo $item-\u003einstance()-\u003ename; // if it's a Term\n    echo $item-\u003einstance()-\u003elink_text; // if it's a custom link\n}\n```\n\nThe `instance()` method will return the matching object:\n\n- `Post` instance for `post` menu item;\n- `Page` instance for `page` menu item;\n- `CustomLink` instance for `custom` menu item;\n- `Term` instance for `category` menu item.\n\n### Multi-levels Menus\n\nTo handle multi-levels menus, loop through all the menu items to put them on the right levels, for example.\n\nYou can use the `MenuItem::parent()` method to retrieve the parent instance of that menu item:\n\n```php\n$items = Menu::slug('foo')-\u003efirst()-\u003eitems;\n$parent = $items-\u003efirst()-\u003eparent(); // Post, Page, CustomLink or Term (category)\n```\n\nTo group menu items according their parents, you can use the `-\u003egroupBy()` method in the `$menu-\u003eitems` collection, grouping menu items by their `$item-\u003eparent()-\u003eID`.\n\nTo read more about the `groupBy()` method [take a look on the Laravel documentation](https://laravel.com/docs/5.4/collections#method-groupby).\n\n## \u003ca id=\"users\"\u003e\u003c/a\u003e Users\n\nYou can manipulate users in the same manner you work with posts:\n\n```php\n// All users\n$users = User::get();\n\n// A specific user\n$user = User::find(1);\necho $user-\u003euser_login;\n```\n\n## \u003ca id=\"auth\"\u003e\u003c/a\u003eAuthentication\n\n### Using Laravel\n\nIf you're using Laravel 5.4 or older, make sure you have the [`CorcelServiceProvider` provider registered](#config-service-loader).\n\nAnd then, define the user provider in `config/auth.php` to allow Laravel to login with WordPress users:\n\n```php\n'providers' =\u003e [\n    'users' =\u003e [\n        'driver' =\u003e 'corcel',\n        'model'  =\u003e Corcel\\Model\\User::class,\n    ],\n],\n```\n\nNow you can use the `Auth` facade to authenticate users:\n\n```php\nAuth::validate([\n    'email' =\u003e 'admin@example.com', // or using 'username' too\n    'password' =\u003e 'secret',\n]);\n```\n\nTo 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.php` from:\n\n```php\nuse App\\Http\\Controllers\\Controller;\nuse Illuminate\\Foundation\\Auth\\ResetsPasswords;\n\nclass PasswordController extends Controller\n{\n    use ResetsPasswords;\n```\n\nto\n\n```php\nuse App\\Http\\Controllers\\Controller;\nuse Illuminate\\Foundation\\Auth\\ResetsPasswords;\nuse Corcel\\Laravel\\Auth\\ResetsPasswords as CorcelResetsPasswords;\n\nclass PasswordController extends Controller\n{\n    use ResetsPasswords, CorcelResetsPasswords {\n        CorcelResetsPasswords::resetPassword insteadof ResetsPasswords;\n    }\n```\n\n### Not using Laravel\n\nYou can use the `AuthUserProvider` class to manually authenticate a user :\n\n```php\n$userProvider = new Corcel\\Laravel\\Auth\\AuthUserProvider;\n$user = $userProvider-\u003eretrieveByCredentials(['username' =\u003e 'admin']);\nif(!is_null($user) \u0026\u0026 $userProvider-\u003evalidateCredentials($user, ['password' =\u003e 'admin'])) {\n    // successfully login\n}\n```\n\n\u003e Remember you can use both `username` and `email` as credentials for a User.\n\n# \u003ca id=\"tests\"\u003e\u003c/a\u003e Running Tests\n\nTo run the phpunit tests, execute the following command :\n\n```\n./vendor/bin/phpunit\n```\n\nIf you have the global `phpunit` command installed you can just type:\n\n```\nphpunit\n```\n\nAll tests were written using Sqlite with `:memory` database, so it runs in your memory. All tests use `factories` and `migrations`. Take a look on `tests/database/factories` and `tests/database/migrations` directories for more information.\n\n# \u003ca id=\"contrib\"\u003e\u003c/a\u003e Contributing\n\nAll contributions are welcome to help improve Corcel.\n\nBefore you submit your Pull Request (PR) consider the following guidelines:\n\n- Fork https://github.com/corcel/corcel in Github;\n\n- 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`;\n\n- 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.\n\n- Run the unit tests locally to make sure your changes did not break any other piece of code;\n\n- Push your new branch to your forked repository, usually `git push origin HEAD` should work;\n\n- 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;\n\n- Wait for the approval :-)\n\n## \u003ca id=\"license\"\u003e\u003c/a\u003e Licence\n\n[MIT License](http://jgrossi.mit-license.org/) © Junior Grossi\n","funding_links":["https://ko-fi.com/A36513JF"],"categories":["PHP","Uncategorized","成品软件"],"sub_categories":["Wordpress","Uncategorized","CMS"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorcel%2Fcorcel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcorcel%2Fcorcel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorcel%2Fcorcel/lists"}