├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
└── src
├── Fbf
└── LaravelBlog
│ └── LaravelBlogServiceProvider.php
├── config
├── administrator
│ └── posts.php
├── images.php
├── link.php
├── meta.php
├── routes.php
├── seed.php
├── views.php
└── you_tube.php
├── controllers
└── PostsController.php
├── lang
├── .gitkeep
└── en
│ └── messages.php
├── migrations
├── .gitkeep
└── 2013_08_21_143501_create_posts_table.php
├── models
└── Post.php
├── routes.php
├── seeds
└── PostTableFakeSeeder.php
└── views
├── partials
├── adjacent.blade.php
├── archives.blade.php
├── details.blade.php
├── list.blade.php
└── share.blade.php
└── posts
├── index.blade.php
└── view.blade.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.3
5 | - 5.4
6 | - 5.5
7 |
8 | before_script:
9 | - curl -s http://getcomposer.org/installer | php
10 | - php composer.phar install --dev
11 |
12 | script: phpunit
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Five by Five
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Laravel Blog
2 | ============
3 |
4 | A Laravel 4 package to add a simple blog to a site
5 |
6 | ## Features
7 |
8 | * Paginated index view with configurable results per page
9 | * Year/Month archive filtering, including an archive partial that you can include in your own views (requires MySQL)
10 | * Draft/Approved status and published date fields for approvals and scheduled publishing of posts
11 | * Configurable URLs, e.g. /blog or /news/yyyy/mm or /articles/< article slug >
12 | * Fields for title, slug, main image or YouTube Video ID, summary, content, link text and url for external links, is sticky?, published date, status, meta description and keywords, in RSS?
13 | * Faker seed to seed your blog with loads of good test data
14 | * Administrator config file for use with FrozenNode's Administrator package
15 | * Bundled migration for building the database schema
16 | * Extendable to add categories or tags relationships and filter by them, see below
17 | *
18 |
19 | ## Installation
20 |
21 | Add the following to you composer.json file (Recommend swapping "dev-master" for the latest release)
22 |
23 | "fbf/laravel-blog": "dev-master"
24 |
25 | Run
26 |
27 | composer update
28 |
29 | Add the following to app/config/app.php
30 |
31 | 'Fbf\LaravelBlog\LaravelBlogServiceProvider'
32 |
33 | Run the package migration
34 |
35 | php artisan migrate --package=fbf/laravel-blog
36 |
37 | Publish the config
38 |
39 | php artisan config:publish fbf/laravel-blog
40 |
41 | Optionally tweak the settings in the many config files for your app
42 |
43 | Optionally copy the administrator config file (`src/config/administrator/posts.php`) to your administrator model config directory.
44 |
45 | Create the relevant image upload directories that you specify in your config, e.g.
46 |
47 | public/uploads/packages/fbf/laravel-blog/main_image/original
48 | public/uploads/packages/fbf/laravel-blog/main_image/thumbnail
49 | public/uploads/packages/fbf/laravel-blog/main_image/resized
50 |
51 | ## Faker seed
52 |
53 | The package comes with a seed that can populate the table with a whole bunch of sample posts. There are some configuration options for the seed in the config file. To run it:
54 |
55 | php artisan db:seed --class="Fbf\LaravelBlog\PostTableFakeSeeder"
56 |
57 | ## Configuration
58 |
59 | See the many configuration options in the files in the config directory
60 |
61 | ## Administrator
62 |
63 | You can use the excellent Laravel Administrator package by FrozenNode to administer your blog.
64 |
65 | http://administrator.frozennode.com/docs/installation
66 |
67 | A ready-to-use model config file for the Post model (posts.php) is provided in the src/config/administrator directory of the package, which you can copy into the app/config/administrator directory (or whatever you set as the model_config_path in the administrator config file).
68 |
69 | ## Usage
70 |
71 | The package should work out the box (provided you have a master blade layout file, since the out-of-the-box views extend this)
72 | but if you want to add other content to the pages, such as your own header, logo, navigation, sidebar etc, you'll want to
73 | override the views provided.
74 |
75 | The package views declare several sections that you may want to `yield` in your `app/views/layouts/master.blade.php` file, e.g.:
76 |
77 | ```html
78 |
79 |
80 |
81 | @yield('title')
82 |
83 |
84 |
85 |
86 |
87 | @yield('content')
88 |
89 |
90 |
91 | ```
92 |
93 | The package's views are actually really simple, and most of the presentation is done in partials. This is deliberate so you
94 | can override the package's views in your own app, so you can include your own chrome, navigation and sidebars etc, yet
95 | you can also still make use of the partials provided, if you want to.
96 |
97 | To override any view in your own app, just create the following directories and copy the file from the package into it, then hack away
98 | * `app/views/packages/fbf/laravel-blog/posts`
99 | * `app/views/packages/fbf/laravel-blog/partials`
100 |
101 | ## Extending the package
102 |
103 | This can be done for the purposes of say, relating the Post model to a Category model and allowing filtering by category,
104 | or relating the Post model to a User model to add and Author to a Post, or simply just for overriding the functionality
105 | in the bundled Post model.
106 |
107 | ### Basic approach
108 |
109 | (See the example below for more specific information.)
110 |
111 | To override the `Post` model in the package, create a model in you app/models directory that extends the package model.
112 |
113 | Finally, update the IoC Container to inject an instance of your model into the `Fbf\LaravelBlog\PostsController`,
114 | instead of the package's model, e.g. in `app/start/global.php`
115 |
116 | ```php
117 | App::bind('Fbf\LaravelBlog\PostsController', function() {
118 | return new Fbf\LaravelBlog\PostsController(new Post);
119 | });
120 | ```
121 |
122 | To achieve adding a relationship and then filtering by that relationship, this involves extending the `Post` model to
123 | add the relationship method that you want, to another existing model in your app, and overriding the
124 | `Post::indexByRelationship()` method.
125 |
126 | In addition, you'll need to create a link between the Post model and the related model. Depending on whether you are going for
127 | a `belongsTo` relationship or a `belongsToMany` relationship, this will either involve adding a field to the `fbf_posts`
128 | table, e.g. `category_id` or creating a many-to-many join table between the `fbf_posts` table and the related model's
129 | table, e.g. `post_tag`. So create a migration for this and run it.
130 |
131 | If you are using FrozenNode's Administrator package, update the `app/config/administrator/posts.php` config file to use
132 | your new model e.g. `Post` instead of `Fbf\LaravelBlog\Post`, and add the field to the `edit_fields` section to allow
133 | you to attach the Post to the related model.
134 |
135 | Finally you need to enable the filter by relationship route. In the package's routes config file in `src/config/routes.php`
136 | change the `'relationship_uri_prefix'` value from false to the string you'd like to use in the URL, after the base part,
137 | and before the 'relationship identifier'. For example, if you want your URLs to be `http://domain.com/posts/tagged/my-tag`
138 | change the `'relationship_uri_prefix'` value to be `'tagged'`.
139 |
140 | ### Example for Post belongsToMany Categories (hierarchical)
141 |
142 | This is a sample model class that demonstrates how you can add a many to many relationship to the Post model and
143 | allow filtering of posts by an item in the related model. This example relates the Post model to a Category model
144 | in the `fbf/laravel-categories` package, which is a hierarchy, so this not only allows you to filter posts to those
145 | assigned to a selected category, but to filter them by the selected category and/or any of it's children.
146 |
147 | To implement as is, create a file in `app/models/Post.php`, and copy this code into it
148 |
149 | ```php
150 | belongsToMany('Fbf\LaravelCategories\Category', 'category_post');
164 | }
165 |
166 | /**
167 | * Query scope to filter posts by a given category
168 | *
169 | * @param $query
170 | * @param $categorySlug
171 | * @throws InvalidArgumentException
172 | * @return mixed
173 | */
174 | public function scopeByRelationship($query, $categorySlug)
175 | {
176 | $category = Category::live()
177 | ->where('slug', '=', $categorySlug)
178 | ->first();
179 |
180 | if (!$category)
181 | {
182 | throw new InvalidArgumentException('Category not found');
183 | }
184 |
185 | return $query->join('category_post', $this->getTable().'.id', '=', 'category_post.post_id')
186 | ->join('fbf_categories', 'category_post.category_id', '=', 'fbf_categories.id')
187 | ->whereBetween('fbf_categories.lft', array($category->lft, $category->rgt))
188 | ->groupBy($this->getTable().'.id');
189 | }
190 |
191 | }
192 | ```
193 |
194 | Here is a sample migration to link Posts to Categories. Create a migration called `2014_02_25_154025_link_posts_categories.php`
195 | and paste this code into it, then run the migration.
196 |
197 | ```php
198 | integer('category_id');
215 | $table->integer('post_id');
216 | });
217 | }
218 |
219 | /**
220 | * Reverse the migrations.
221 | *
222 | * @return void
223 | */
224 | public function down()
225 | {
226 | Schema::drop('category_post');
227 | }
228 |
229 | }
230 | ```
231 |
232 | If you are using FrozenNode's Administrator package, update the `app/config/administrator/posts.php` config file to use
233 | your new model e.g. `Post` instead of `Fbf\LaravelBlog\Post`, and add the field to the `edit_fields` section to allow
234 | you to attach the Post to the related model.
235 |
236 | ```php
237 | /**
238 | * The class name of the Eloquent model that this config represents
239 | *
240 | * @type string
241 | */
242 | 'model' => 'Post',
243 | ```
244 | ...
245 | ```php
246 | 'categories' => array(
247 | 'title' => 'Categories',
248 | 'type' => 'relationship',
249 | 'name_field' => 'path',
250 | 'options_sort_field' => 'name',
251 | ),
252 | ```
253 |
254 | As mentioned above, update the IoC Container to inject an instance of your model into the `Fbf\LaravelBlog\PostsController`,
255 | instead of the package's model, e.g. in `app/start/global.php`
256 |
257 | ```php
258 | App::bind('Fbf\LaravelBlog\PostsController', function() {
259 | return new Fbf\LaravelBlog\PostsController(new Post);
260 | });
261 | ```
262 |
263 | Finally you need to enable the 'filter by relationship' route. In the package's routes config file in `src/config/routes.php`
264 | change the `'relationship_uri_prefix'` value from false to `'category'`.
265 |
266 | Once you've created some categories and assigned some posts to them, you can filter the posts by visiting a URL like
267 | `http://domain.com/blog/category/my-category`.
268 |
269 | ### Need comments?
270 |
271 | Have a look at https://github.com/FbF/Laravel-Comments
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fbf/laravel-blog",
3 | "description": "A Laravel 4 package to add a simple blog to a site",
4 | "keywords": ["laravel","blog"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Neil Crookes",
9 | "email": "neil.crookes@fivebyfiveuk.com"
10 | }
11 | ],
12 | "require": {
13 | "php": ">=5.3.0",
14 | "illuminate/support": "4.*",
15 | "thujohn/rss": "dev-master",
16 | "cviebrock/eloquent-sluggable": "1.0.*",
17 | "fzaninotto/faker": "1.4.*"
18 | },
19 | "autoload": {
20 | "classmap": [
21 | "src/controllers",
22 | "src/migrations",
23 | "src/models",
24 | "src/seeds"
25 | ],
26 | "psr-0": {
27 | "Fbf\\LaravelBlog": "src/"
28 | }
29 | },
30 | "minimum-stability": "dev"
31 | }
32 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Fbf/LaravelBlog/LaravelBlogServiceProvider.php:
--------------------------------------------------------------------------------
1 | package('fbf/laravel-blog');
22 |
23 | if (\Config::get('laravel-blog::routes.use_package_routes', true))
24 | {
25 | include __DIR__.'/../../routes.php';
26 | }
27 |
28 | \App::register('Thujohn\Rss\RssServiceProvider');
29 | \App::register('Cviebrock\EloquentSluggable\SluggableServiceProvider');
30 |
31 | // Shortcut so developers don't need to add an Alias in app/config/app.php
32 | $this->app->booting(function()
33 | {
34 | $loader = \Illuminate\Foundation\AliasLoader::getInstance();
35 | $loader->alias('Fbf\LaravelBlog\Rss', 'Thujohn\Rss\RssFacade');
36 | $loader->alias('Sluggable', 'Cviebrock\EloquentSluggable\Facades\Sluggable');
37 | });
38 |
39 | }
40 |
41 | /**
42 | * Register the service provider.
43 | *
44 | * @return void
45 | */
46 | public function register()
47 | {
48 |
49 | }
50 |
51 | /**
52 | * Get the services provided by the provider.
53 | *
54 | * @return array
55 | */
56 | public function provides()
57 | {
58 | return array();
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/src/config/administrator/posts.php:
--------------------------------------------------------------------------------
1 | 'Posts',
11 |
12 | /**
13 | * The singular name of your model
14 | *
15 | * @type string
16 | */
17 | 'single' => 'post',
18 |
19 | /**
20 | * The class name of the Eloquent model that this config represents
21 | *
22 | * @type string
23 | */
24 | 'model' => 'Fbf\LaravelBlog\Post',
25 |
26 | /**
27 | * The columns array
28 | *
29 | * @type array
30 | */
31 | 'columns' => array(
32 | 'title' => array(
33 | 'title' => 'Title'
34 | ),
35 | 'published_date' => array(
36 | 'title' => 'Published'
37 | ),
38 | 'status' => array(
39 | 'title' => 'Status',
40 | 'select' => "CASE (:table).status WHEN '".Fbf\LaravelBlog\Post::APPROVED."' THEN 'Approved' WHEN '".Fbf\LaravelBlog\Post::DRAFT."' THEN 'Draft' END",
41 | ),
42 | 'updated_at' => array(
43 | 'title' => 'Last Updated'
44 | ),
45 | ),
46 |
47 | /**
48 | * The edit fields array
49 | *
50 | * @type array
51 | */
52 | 'edit_fields' => array(
53 | 'title' => array(
54 | 'title' => 'Title',
55 | 'type' => 'text',
56 | ),
57 | 'slug' => array(
58 | 'title' => 'Slug',
59 | 'type' => 'text',
60 | 'visible' => function($model)
61 | {
62 | return $model->exists;
63 | },
64 | ),
65 | 'main_image' => array(
66 | 'title' => 'Main Image',
67 | 'type' => 'image',
68 | 'naming' => 'random',
69 | 'location' => public_path() . Config::get('laravel-blog::images.main_image.original.dir'),
70 | 'size_limit' => 5,
71 | 'sizes' => array(
72 | array(
73 | Config::get('laravel-blog::images.main_image.sizes.thumbnail.width'),
74 | Config::get('laravel-blog::images.main_image.sizes.thumbnail.height'),
75 | Config::get('laravel-blog::images.main_image.sizes.thumbnail.method'),
76 | public_path() . Config::get('laravel-blog::images.main_image.sizes.thumbnail.dir'),
77 | 100
78 | ),
79 | array(
80 | Config::get('laravel-blog::images.main_image.sizes.resized.width'),
81 | Config::get('laravel-blog::images.main_image.sizes.resized.height'),
82 | Config::get('laravel-blog::images.main_image.sizes.resized.method'),
83 | public_path() . Config::get('laravel-blog::images.main_image.sizes.resized.dir'),
84 | 100
85 | ),
86 | ),
87 | ),
88 | 'main_image_alt' => array(
89 | 'title' => 'Image ALT text',
90 | 'type' => 'text',
91 | ),
92 | 'you_tube_video_id' => array(
93 | 'title' => 'YouTube Video ID (Takes precedence over the image if it\'s populated)',
94 | 'type' => 'text',
95 | ),
96 | 'summary' => array(
97 | 'title' => 'Summary',
98 | 'type' => 'textarea',
99 | 'limit' => 300, //optional, defaults to no limit
100 | 'height' => 130, //optional, defaults to 100
101 | ),
102 | 'content' => array(
103 | 'title' => 'Content',
104 | 'type' => 'wysiwyg',
105 | ),
106 | 'link_text' => array(
107 | 'title' => 'Link Text',
108 | 'type' => 'text',
109 | 'visible' => Config::get('laravel-blog::link.show'),
110 | ),
111 | 'link_url' => array(
112 | 'title' => 'Link URL',
113 | 'type' => 'text',
114 | 'visible' => Config::get('laravel-blog::link.show'),
115 | ),
116 | 'is_sticky' => array(
117 | 'title' => 'Is sticky?',
118 | 'type' => 'bool',
119 | ),
120 | 'in_rss' => array(
121 | 'title' => 'In RSS Feed?',
122 | 'type' => 'bool',
123 | ),
124 | 'page_title' => array(
125 | 'title' => 'Page Title',
126 | 'type' => 'text',
127 | ),
128 | 'meta_description' => array(
129 | 'title' => 'Meta Description',
130 | 'type' => 'textarea',
131 | ),
132 | 'meta_keywords' => array(
133 | 'title' => 'Meta Keywords',
134 | 'type' => 'textarea',
135 | ),
136 | 'published_date' => array(
137 | 'title' => 'Published Date',
138 | 'type' => 'datetime',
139 | 'date_format' => 'yy-mm-dd', //optional, will default to this value
140 | 'time_format' => 'HH:mm', //optional, will default to this value
141 | ),
142 | 'status' => array(
143 | 'type' => 'enum',
144 | 'title' => 'Status',
145 | 'options' => array(
146 | Fbf\LaravelBlog\Post::DRAFT => 'Draft',
147 | Fbf\LaravelBlog\Post::APPROVED => 'Approved',
148 | ),
149 | ),
150 | 'created_at' => array(
151 | 'title' => 'Created',
152 | 'type' => 'datetime',
153 | 'editable' => false,
154 | ),
155 | 'updated_at' => array(
156 | 'title' => 'Updated',
157 | 'type' => 'datetime',
158 | 'editable' => false,
159 | ),
160 | ),
161 |
162 | /**
163 | * The filter fields
164 | *
165 | * @type array
166 | */
167 | 'filters' => array(
168 | 'title' => array(
169 | 'title' => 'Title',
170 | ),
171 | 'summary' => array(
172 | 'type' => 'text',
173 | 'title' => 'Summary',
174 | ),
175 | 'content' => array(
176 | 'type' => 'text',
177 | 'title' => 'Content',
178 | ),
179 | 'published_date' => array(
180 | 'title' => 'Published Date',
181 | 'type' => 'date',
182 | ),
183 | 'status' => array(
184 | 'type' => 'enum',
185 | 'title' => 'Status',
186 | 'options' => array(
187 | Fbf\LaravelBlog\Post::DRAFT => 'Draft',
188 | Fbf\LaravelBlog\Post::APPROVED => 'Approved',
189 | ),
190 | ),
191 | ),
192 |
193 | /**
194 | * The width of the model's edit form
195 | *
196 | * @type int
197 | */
198 | 'form_width' => 500,
199 |
200 | /**
201 | * The validation rules for the form, based on the Laravel validation class
202 | *
203 | * @type array
204 | */
205 | 'rules' => array(
206 | 'title' => 'required|max:255',
207 | 'main_image' => 'max:255',
208 | 'main_image_alt' => 'max:255',
209 | 'you_tube_video_id' => 'max:255',
210 | 'status' => 'required|in:'.Fbf\LaravelBlog\Post::DRAFT.','.Fbf\LaravelBlog\Post::APPROVED,
211 | 'published_date' => 'required|date_format:"Y-m-d H:i:s"|date',
212 | ),
213 |
214 | /**
215 | * The sort options for a model
216 | *
217 | * @type array
218 | */
219 | 'sort' => array(
220 | 'field' => 'updated_at',
221 | 'direction' => 'desc',
222 | ),
223 |
224 | /**
225 | * If provided, this is run to construct the front-end link for your model
226 | *
227 | * @type function
228 | */
229 | 'link' => function($model)
230 | {
231 | return $model->getUrl();
232 | },
233 |
234 | );
235 |
--------------------------------------------------------------------------------
/src/config/images.php:
--------------------------------------------------------------------------------
1 | array(
16 |
17 | /**
18 | * Whether the Main Image field is shown in the administrator config, and whether it's created for the seed
19 | */
20 | 'show' => true,
21 |
22 | /**
23 | * Settings for the original image uploaded
24 | */
25 | 'original' => array(
26 |
27 | /**
28 | * Location, relative to public_path(), where the image is stored.
29 | */
30 | 'dir' => '/uploads/packages/fbf/laravel-blog/main_image/original/',
31 |
32 | ),
33 |
34 | /**
35 | * Settings for the various sizes of the images that are created from the original
36 | */
37 | 'sizes' => array(
38 |
39 | /**
40 | * The thumbnail size is used on the list view
41 | */
42 | 'thumbnail' => array(
43 | 'dir' => '/uploads/packages/fbf/laravel-blog/main_image/thumbnail/',
44 | 'method' => 'crop',
45 | 'width' => 150,
46 | 'height' => 150,
47 | ),
48 |
49 | /**
50 | * The resized size is used on the details view
51 | */
52 | 'resized' => array(
53 | 'dir' => '/uploads/packages/fbf/laravel-blog/main_image/resized/',
54 | 'method' => 'crop',
55 | 'width' => 400,
56 | 'height' => 400,
57 | ),
58 | ),
59 | ),
60 | );
--------------------------------------------------------------------------------
/src/config/link.php:
--------------------------------------------------------------------------------
1 | true,
13 |
14 | );
--------------------------------------------------------------------------------
/src/config/meta.php:
--------------------------------------------------------------------------------
1 | array(
12 |
13 | /**
14 | * Page title of the blog index page
15 | *
16 | * @type string
17 | */
18 | 'page_title' => 'My blog',
19 |
20 | /**
21 | * Meta description of the blog index page
22 | *
23 | * @type string
24 | */
25 | 'meta_description' => 'This is the description for my blog',
26 |
27 | /**
28 | * Meta keywords of the blog index page
29 | *
30 | * @type string
31 | */
32 | 'meta_keywords' => 'These are the keywords for my blog',
33 |
34 | ),
35 |
36 | /**
37 | * Meta configuration for RSS feed (title and description)
38 | */
39 | 'rss_feed' => array(
40 |
41 | /**
42 | * RSS feed title
43 | *
44 | * @type string
45 | */
46 | 'title' => 'My blog',
47 |
48 | /**
49 | * RSS feed description
50 | *
51 | * @type string
52 | */
53 | 'description' => 'This is the description for my blog',
54 |
55 | ),
56 |
57 | );
--------------------------------------------------------------------------------
/src/config/routes.php:
--------------------------------------------------------------------------------
1 | true,
10 |
11 | /**
12 | * Base URI of the package's pages, e.g. "blog" in http://domain.com/blog and http://domain.com/blog/my-post
13 | */
14 | 'base_uri' => 'blog',
15 |
16 | /**
17 | * URI prefix of the blog relationship filter
18 | *
19 | * @type mixed false | string
20 | */
21 | 'relationship_uri_prefix' => false,
22 |
23 | );
--------------------------------------------------------------------------------
/src/config/seed.php:
--------------------------------------------------------------------------------
1 | true,
12 |
13 | /**
14 | * Number of fake posts to create
15 | */
16 | 'number' => 20,
17 |
18 | /**
19 | * Configuration options for YouTube for the fake seeder
20 | */
21 | 'you_tube' => array(
22 |
23 | /**
24 | * One in every X posts is a YouTube Video (use 0 for no YouTube Videos, or set you_tube.show to false)
25 | */
26 | 'freq' => 5,
27 |
28 | /**
29 | * List of the you tube video ids that could be used
30 | */
31 | 'video_ids' => array(
32 | 'dQw4w9WgXcQ'
33 | ),
34 |
35 | ),
36 |
37 | /**
38 | * Configuration options for the images for the fake seeder
39 | */
40 | 'images' => array(
41 |
42 | /**
43 | * Configuration options for the main image for the fake seeder
44 | */
45 | 'main_image' => array(
46 |
47 | /**
48 | * One in every X posts that is not a YouTube Video, has a main image (use 0 for no main images, or set
49 | * images.main_image.show to false)
50 | */
51 | 'freq' => 2,
52 |
53 | /**
54 | * Lorem Pixel category to use for the main image
55 | */
56 | 'category' => 'abstract',
57 |
58 | /**
59 | * Width of the original image to fetch by the fake seeder
60 | */
61 | 'original_width' => 400,
62 |
63 | /**
64 | * Height of the original image to fetch by the fake seeder
65 | */
66 | 'original_height' => 400,
67 |
68 | ),
69 |
70 | ),
71 |
72 | /**
73 | * Configuration options for the seeder links
74 | */
75 | 'link' => array(
76 |
77 | /**
78 | * One in every X items has a link (use 0 for no links)
79 | */
80 | 'freq' => 2,
81 |
82 | /**
83 | * Options for the link text
84 | */
85 | 'texts' => array(
86 | 'Visit website',
87 | 'Read more',
88 | 'Find out more',
89 | 'Go to the official site',
90 | 'Check this out'
91 | ),
92 |
93 | /**
94 | * Options for the link urls
95 | */
96 | 'urls' => array(
97 | 'http://www.google.com',
98 | 'http://www.bbc.co.uk',
99 | 'http://www.fivebyfiveuk.com',
100 | ),
101 |
102 | ),
103 |
104 | );
--------------------------------------------------------------------------------
/src/config/views.php:
--------------------------------------------------------------------------------
1 | 'j\<\s\u\p\>S\<\/\s\u\p\> F \'y',
15 |
16 | /**
17 | * Configuration options for the index page
18 | */
19 | 'index_page' => array(
20 |
21 | /**
22 | * The view to use for the blog index pages (standard default index, index by year/month and index by
23 | * relationship). You can change this to a view in your app, and inside your own view you can @include the
24 | * various partials in the package if you want to, or you can use this one provided, provided you have a blade
25 | * layout called `master`, or you can leave this setting as it is, but create a new view file inside you own app
26 | * at `app/views/packages/fbf/laravel-blog/posts/index.blade.php`.
27 | *
28 | * @type string
29 | */
30 | 'view' => 'laravel-blog::posts.index',
31 |
32 | /**
33 | * Determines whether to show the archives on the index page
34 | *
35 | * @type bool
36 | */
37 | 'show_archives' => true,
38 |
39 | /**
40 | * The number of items to show per page on the index page
41 | *
42 | * @type int
43 | */
44 | 'results_per_page' => 4,
45 |
46 | ),
47 |
48 | /**
49 | * Configuration options for the view page
50 | */
51 | 'view_page' => array(
52 |
53 | /**
54 | * The view to use for the blog post detail page. You can change this to a view in your app, and inside your own
55 | * view you can @include the various partials in the package if you want to, or you can use this one provided,
56 | * provided you have a blade layout called `master`, or you can leave this setting as it is, but create a new
57 | * view file inside you own app at `app/views/packages/fbf/laravel-blog/posts/view.blade.php`.
58 | */
59 | 'view' => 'laravel-blog::posts.view',
60 |
61 | /**
62 | * Determines whether to show the archives on the view page
63 | *
64 | * @type bool
65 | */
66 | 'show_archives' => true,
67 |
68 | /**
69 | * Determines whether to show links to adjacent (i.e. previous and next) items on the view page
70 | *
71 | * @type bool
72 | */
73 | 'show_adjacent_items' => true,
74 |
75 | /**
76 | * Determines whether to show the share partial on the post view page. Note, if you want to change the share
77 | * partial, just override it by creating a new file at `app/views/packages/fbf/laravel-blog/partials/share.blade.php`
78 | *
79 | * @type bool
80 | */
81 | 'show_share_partial' => true,
82 |
83 | ),
84 |
85 | );
--------------------------------------------------------------------------------
/src/config/you_tube.php:
--------------------------------------------------------------------------------
1 | true,
12 |
13 | /**
14 | * YouTube Embed Player code used if an item has a You Tube Video ID set
15 | * instead of a Main Image. Changing the settings will apply to all items that
16 | * have a You Tube Video on them. The placeholder "%YOU_TUBE_VIDEO_ID%" is
17 | * replaced with the You Tube Video ID in the database for this item.
18 | */
19 | 'embed_code' => '',
20 |
21 | /**
22 | * YouTube Thumbnail code used if an item has a You Tube Video ID set instead of a Main Image. Changing the
23 | * settings will apply to all entries on the index pages for items that have a You Tube Video. The
24 | * placeholder "%YOU_TUBE_VIDEO_ID%" is replaced with the You Tube Video ID in the database for this item.
25 | */
26 | 'thumbnail_code' => '
',
27 |
28 | );
--------------------------------------------------------------------------------
/src/controllers/PostsController.php:
--------------------------------------------------------------------------------
1 | post = $post;
16 | }
17 |
18 | /**
19 | * @return mixed
20 | */
21 | public function index()
22 | {
23 | // Get the selected posts
24 | $posts = $this->post->live()
25 | ->orderBy($this->post->getTable().'.is_sticky', 'desc')
26 | ->orderBy($this->post->getTable().'.published_date', 'desc')
27 | ->paginate(\Config::get('laravel-blog::views.index_page.results_per_page'));
28 |
29 | // Get the archives data if the config says to show the archives on the index page
30 | if (\Config::get('laravel-blog::views.index_page.show_archives'))
31 | {
32 | $archives = $this->post->archives();
33 | }
34 |
35 | return \View::make(\Config::get('laravel-blog::views.index_page.view'), compact('posts', 'archives'));
36 | }
37 |
38 | /**
39 | * @param $selectedYear
40 | * @param $selectedMonth
41 | * @return mixed
42 | */
43 | public function indexByYearMonth($selectedYear, $selectedMonth)
44 | {
45 | // Get the selected posts
46 | $posts = $this->post->live()
47 | ->byYearMonth($selectedYear, $selectedMonth)
48 | ->orderBy($this->post->getTable().'.is_sticky', 'desc')
49 | ->orderBy($this->post->getTable().'.published_date', 'desc')
50 | ->paginate(\Config::get('laravel-blog::views.index_page.results_per_page'));
51 |
52 | // Get the archives data if the config says to show the archives on the index page
53 | if (\Config::get('laravel-blog::views.index_page.show_archives'))
54 | {
55 | $archives = $this->post->archives();
56 | }
57 |
58 | return \View::make(\Config::get('laravel-blog::views.index_page.view'), compact('posts', 'selectedYear', 'selectedMonth', 'archives'));
59 | }
60 |
61 | /**
62 | * @param $relationshipIdentifier
63 | * @return mixed
64 | */
65 | public function indexByRelationship($relationshipIdentifier)
66 | {
67 | // Get the selected posts
68 | $posts = $this->post->live()
69 | ->byRelationship($relationshipIdentifier)
70 | ->orderBy($this->post->getTable().'.is_sticky', 'desc')
71 | ->orderBy($this->post->getTable().'.published_date', 'desc')
72 | ->paginate(\Config::get('laravel-blog::views.index_page.results_per_page'));
73 |
74 | // Get the archives data if the config says to show the archives on the index page
75 | if (\Config::get('laravel-blog::views.index_page.show_archives'))
76 | {
77 | $archives = $this->post->archives();
78 | }
79 |
80 | return \View::make(\Config::get('laravel-blog::views.index_page.view'), compact('posts', 'archives'));
81 | }
82 |
83 | /**
84 | * @param $slug
85 | * @return mixed
86 | */
87 | public function view($slug)
88 | {
89 | // Get the selected post
90 | $post = $this->post->live()
91 | ->where($this->post->getTable().'.slug', '=', $slug)
92 | ->firstOrFail();
93 |
94 | // Get the next newest and next oldest post if the config says to show these links on the view page
95 | $newer = $older = false;
96 | if (\Config::get('laravel-blog::views.view_page.show_adjacent_items'))
97 | {
98 | $newer = $post->newer();
99 | $older = $post->older();
100 | }
101 |
102 | // Get the archives data if the config says to show the archives on the view page
103 | if (\Config::get('laravel-blog::views.view_page.show_archives'))
104 | {
105 | $archives = $this->post->archives();
106 | }
107 |
108 | return \View::make(\Config::get('laravel-blog::views.view_page.view'), compact('post', 'newer', 'older', 'archives'));
109 |
110 | }
111 |
112 | /**
113 | * @return mixed
114 | */
115 | public function rss()
116 | {
117 | $feed = Rss::feed('2.0', 'UTF-8');
118 | $feed->channel(array(
119 | 'title' => \Config::get('laravel-blog::meta.rss_feed.title'),
120 | 'description' => \Config::get('laravel-blog::meta.rss_feed.description'),
121 | 'link' => \URL::current(),
122 | ));
123 | $posts = $this->post->live()
124 | ->where($this->post->getTable().'.in_rss', '=', true)
125 | ->orderBy($this->post->getTable().'.published_date', 'desc')
126 | ->take(10)
127 | ->get();
128 | foreach ($posts as $post){
129 | $feed->item(array(
130 | 'title' => $post->title,
131 | 'description' => $post->summary,
132 | 'link' => \URL::action('Fbf\LaravelBlog\PostsController@view', array('slug' => $post->slug)),
133 | ));
134 | }
135 | return \Response::make($feed, 200, array('Content-Type', 'application/rss+xml'));
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/lang/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FbF/Laravel-Blog/d731e710664fbc8c11a4162804bc77888b5a3743/src/lang/.gitkeep
--------------------------------------------------------------------------------
/src/lang/en/messages.php:
--------------------------------------------------------------------------------
1 | array(
6 | 'more_link_text' => 'Read more',
7 | 'no_items' => 'Sorry, there are no articles.'
8 | ),
9 |
10 | 'details' => array(
11 | 'share_label' => 'Share',
12 | 'all_link_text' => 'All posts',
13 | ),
14 |
15 | 'share' => array(
16 | 'facebook' => 'Share on Facebook',
17 | 'twitter' => 'Share on Twitter',
18 | 'label' => 'Share',
19 | ),
20 |
21 | 'adjacent' => array(
22 | 'next_link_text' => '« :title',
23 | 'prev_link_text' => ':title »',
24 | ),
25 |
26 | );
--------------------------------------------------------------------------------
/src/migrations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FbF/Laravel-Blog/d731e710664fbc8c11a4162804bc77888b5a3743/src/migrations/.gitkeep
--------------------------------------------------------------------------------
/src/migrations/2013_08_21_143501_create_posts_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('title');
18 | $table->string('main_image')->nullable();
19 | $table->string('main_image_alt')->nullable();
20 | $table->string('you_tube_video_id')->nullable();
21 | $table->text('summary');
22 | $table->text('content');
23 | $table->string('link_text')->nullable();
24 | $table->string('link_url')->nullable();
25 | $table->boolean('in_rss');
26 | $table->boolean('is_sticky');
27 | $table->string('slug')->unique();
28 | $table->string('page_title');
29 | $table->text('meta_description');
30 | $table->text('meta_keywords');
31 | $table->enum('status', array('DRAFT', 'APPROVED'))->default('DRAFT');
32 | $table->dateTime('published_date')->nullable();
33 | $table->dateTime('deleted_at')->nullable();
34 | $table->timestamps();
35 | });
36 | }
37 |
38 | /**
39 | * Reverse the migrations.
40 | *
41 | * @return void
42 | */
43 | public function down()
44 | {
45 | Schema::drop('fbf_blog_posts');
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/models/Post.php:
--------------------------------------------------------------------------------
1 | 'title',
37 | 'save_to' => 'slug',
38 | 'separator' => '-',
39 | 'unique' => true,
40 | 'include_trashed' => true,
41 | );
42 |
43 | /**
44 | * Query scope for "live" posts, adds conditions for status = APPROVED and published date is in the past
45 | *
46 | * @param $query
47 | * @return mixed
48 | */
49 | public function scopeLive($query)
50 | {
51 | return $query->where($this->getTable().'.status', '=', Post::APPROVED)
52 | ->where($this->getTable().'.published_date', '<=', \Carbon\Carbon::now());
53 | }
54 |
55 | /**
56 | * Query scope for posts published within the given year and month number.
57 | *
58 | * @param $query
59 | * @param $year
60 | * @param $month
61 | * @return mixed
62 | */
63 | public function scopeByYearMonth($query, $year, $month)
64 | {
65 | return $query->where(\DB::raw('DATE_FORMAT(published_date, "%Y%m")'), '=', $year.$month);
66 | }
67 |
68 | /**
69 | * Returns a formatted multi-dimensional array, indexed by year and month number, each with an array with keys for
70 | * 'month name' and the count / number of items in that month. For example:
71 | *
72 | * array(
73 | * 2014 => array(
74 | * 01 => array(
75 | * 'monthname' => 'January',
76 | * 'count' => 4,
77 | * ),
78 | * 02 => array(
79 | * 'monthname' => 'February',
80 | * 'count' => 3,
81 | * ),
82 | * )
83 | * )
84 | *
85 | * @return array
86 | */
87 | public static function archives()
88 | {
89 | // Get the data
90 | $archives = self::live()
91 | ->select(\DB::raw('
92 | YEAR(`published_date`) AS `year`,
93 | DATE_FORMAT(`published_date`, "%m") AS `month`,
94 | MONTHNAME(`published_date`) AS `monthname`,
95 | COUNT(*) AS `count`
96 | '))
97 | ->groupBy(\DB::raw('DATE_FORMAT(`published_date`, "%Y%m")'))
98 | ->orderBy('published_date', 'desc')
99 | ->get();
100 |
101 | // Convert it to a nicely formatted array so we can easily render the view
102 | $results = array();
103 | foreach ($archives as $archive)
104 | {
105 | $results[$archive->year][$archive->month] = array(
106 | 'monthname' => $archive->monthname,
107 | 'count' => $archive->count,
108 | );
109 | }
110 | return $results;
111 | }
112 |
113 | /**
114 | * To filter posts by a relationship, you should extend the Post class and define both the relationship and the
115 | * scopeByRelationship() method in your subclass. See the package readme for an example.
116 | *
117 | * @param $query
118 | * @param $relationshipIdentifier
119 | * @throws Exception
120 | */
121 | public function scopeByRelationship($query, $relationshipIdentifier)
122 | {
123 | throw new Exception('Extend this class and override this method according to your app\s requirements');
124 | }
125 |
126 | /**
127 | * Returns the HTML img tag for the requested image type and size for this item
128 | *
129 | * @param $type
130 | * @param $size
131 | * @return null|string
132 | */
133 | public function getImage($type, $size, array $attributes = array())
134 | {
135 | if (empty($this->$type))
136 | {
137 | return null;
138 | }
139 | $html = '
{$type.'_alt'} . '"';
141 | $html .= ' width="' . $this->getImageWidth($type, $size) . '"';
142 | $html .= ' height="' . $this->getImageHeight($type, $size) . '"';
143 |
144 | $html_attributes = '';
145 | if (!empty($attributes)) {
146 | $html_attributes = join(' ', array_map(function($key) use ($attributes) {
147 | if(is_bool($attributes[$key]))
148 | {
149 | return $attributes[$key] ? $key : '';
150 | }
151 | return "{$key}=\"{$attributes[$key]}\"";
152 | }, array_keys($attributes)));
153 | }
154 |
155 | return $html;
156 | }
157 |
158 | /**
159 | * Returns the value for use in the src attribute of an img tag for the given image type and size
160 | *
161 | * @param $type
162 | * @param $size
163 | * @return null|string
164 | */
165 | public function getImageSrc($type, $size)
166 | {
167 | if (empty($this->$type))
168 | {
169 | return null;
170 | }
171 | return $this->getImageConfig($type, $size, 'dir') . $this->$type;
172 | }
173 |
174 | /**
175 | * Returns the value for use in the width attribute of an img tag for the given image type and size
176 | *
177 | * @param $type
178 | * @param $size
179 | * @return null|string
180 | */
181 | public function getImageWidth($type, $size)
182 | {
183 | if (empty($this->$type))
184 | {
185 | return null;
186 | }
187 | $method = $this->getImageConfig($type, $size, 'method');
188 |
189 | // Width varies for images that are 'portrait', 'auto', 'fit', 'crop'
190 | if (in_array($method, array('portrait', 'auto', 'fit', 'crop')))
191 | {
192 | list($width) = $this->getImageDimensions($type, $size);
193 | return $width;
194 | }
195 | return $this->getImageConfig($type, $size, 'width');
196 | }
197 |
198 | /**
199 | * Returns the value for use in the height attribute of an img tag for the given image type and size
200 | *
201 | * @param $type
202 | * @param $size
203 | * @return null|string
204 | */
205 | public function getImageHeight($type, $size)
206 | {
207 | if (empty($this->$type))
208 | {
209 | return null;
210 | }
211 | $method = $this->getImageConfig($type, $size, 'method');
212 |
213 | // Height varies for images that are 'landscape', 'auto', 'fit', 'crop'
214 | if (in_array($method, array('landscape', 'auto', 'fit', 'crop')))
215 | {
216 | list($width, $height) = $this->getImageDimensions($type, $size);
217 | return $height;
218 | }
219 | return $this->getImageConfig($type, $size, 'height');
220 | }
221 |
222 | /**
223 | * Returns an array of the width and height of the current instance's image $type and $size
224 | *
225 | * @param $type
226 | * @param $size
227 | * @return array
228 | */
229 | protected function getImageDimensions($type, $size)
230 | {
231 | $pathToImage = public_path($this->getImageConfig($type, $size, 'dir') . $this->$type);
232 | if (is_file($pathToImage) && file_exists($pathToImage))
233 | {
234 | list($width, $height) = getimagesize($pathToImage);
235 | }
236 | else
237 | {
238 | $width = $height = false;
239 | }
240 | return array($width, $height);
241 | }
242 |
243 | /**
244 | * Returns the config setting for an image
245 | *
246 | * @param $imageType
247 | * @param $size
248 | * @param $property
249 | * @internal param $type
250 | * @return mixed
251 | */
252 | public function getImageConfig($imageType, $size, $property)
253 | {
254 | $config = $this->getConfigPrefix().'images.' . $imageType . '.';
255 | if ($size == 'original')
256 | {
257 | $config .= 'original.';
258 | }
259 | elseif (!is_null($size))
260 | {
261 | $config .= 'sizes.' . $size . '.';
262 | }
263 | $config .= $property;
264 | return \Config::get($config);
265 | }
266 |
267 | /**
268 | * Helper function to determine whether the item has a YouTube video
269 | *
270 | * @return bool
271 | */
272 | public function hasYouTubeVideo()
273 | {
274 | return !empty($this->you_tube_video_id);
275 | }
276 |
277 | /**
278 | * Returns the thumbnail image code defined in the config, for the current item's you tube video id
279 | *
280 | * @return string
281 | */
282 | public function getYouTubeThumbnailImage()
283 | {
284 | return str_replace('%YOU_TUBE_VIDEO_ID%', $this->you_tube_video_id, \Config::get($this->getConfigPrefix().'you_tube.thumbnail_code'));
285 | }
286 |
287 | /**
288 | * Returns the embed code defined in the config, for the current item's you tube video id
289 | *
290 | * @return string
291 | */
292 | public function getYouTubeEmbedCode()
293 | {
294 | return str_replace('%YOU_TUBE_VIDEO_ID%', $this->you_tube_video_id, \Config::get($this->getConfigPrefix().'you_tube.embed_code'));
295 | }
296 |
297 | /**
298 | * Returns the published date formatted according to the config setting
299 | * @return string
300 | */
301 | public function getDate()
302 | {
303 | return date(\Config::get($this->getConfigPrefix().'views.published_date_format'), strtotime($this->published_date));
304 | }
305 |
306 | /**
307 | * Returns the URL of the post
308 | * @return string
309 | */
310 | public function getUrl()
311 | {
312 | return \URL::action('Fbf\LaravelBlog\PostsController@view', array('slug' => $this->slug));
313 | }
314 |
315 | /**
316 | * Help function to determine whether the item has a link
317 | * @return bool
318 | */
319 | public function hasLink()
320 | {
321 | return !empty($this->link_text) && !empty($this->link_url);
322 | }
323 |
324 | /**
325 | * Returns the next newer post, relative to the current one, if it exists
326 | * @return mixed
327 | */
328 | public function newer()
329 | {
330 | return $this->live()
331 | ->where('published_date', '>=', $this->published_date)
332 | ->where('id', '<>', $this->id)
333 | ->orderBy('published_date', 'asc')
334 | ->orderBy('id', 'asc')
335 | ->first();
336 | }
337 |
338 | /**
339 | * Returns the next older post, relative to the current one, if it exists
340 | * @return mixed
341 | */
342 | public function older()
343 | {
344 | return $this->live()
345 | ->where('published_date', '<=', $this->published_date)
346 | ->where('id', '<>', $this->id)
347 | ->orderBy('published_date', 'desc')
348 | ->orderBy('id', 'desc')
349 | ->first();
350 | }
351 |
352 | /**
353 | * Returns the config prefix
354 | *
355 | * @return string
356 | */
357 | public function getConfigPrefix()
358 | {
359 | return $this->configPrefix;
360 | }
361 |
362 | /**
363 | * Sets the config prefix string
364 | *
365 | * @param $configBase string
366 | * @return string
367 | */
368 | public function setConfigPrefix($configBase)
369 | {
370 | return $this->configPrefix = $configBase;
371 | }
372 |
373 | }
374 |
--------------------------------------------------------------------------------
/src/routes.php:
--------------------------------------------------------------------------------
1 | where(array('year' => '\d{4}', 'month' => '\d{2}'));
8 |
9 | if (Config::get('laravel-blog::routes.relationship_uri_prefix'))
10 | {
11 | // Relationship filtered listing, e.g. by category or tag, e.g. http://domain.com/blog/category/my-category
12 | Route::get(Config::get('laravel-blog::routes.base_uri').'/'.Config::get('laravel-blog::routes.relationship_uri_prefix').'/{relationshipIdentifier}', 'Fbf\LaravelBlog\PostsController@indexByRelationship');
13 | }
14 |
15 | // Blog post detail page e.g. http://domain.com/blog/my-post
16 | Route::get(Config::get('laravel-blog::routes.base_uri').'/{slug}', 'Fbf\LaravelBlog\PostsController@view');
17 |
18 | // RSS feed URL e.g. http://domain.com/blog.rss
19 | Route::get(Config::get('laravel-blog::routes.base_uri').'.rss', 'Fbf\LaravelBlog\PostsController@rss');
--------------------------------------------------------------------------------
/src/seeds/PostTableFakeSeeder.php:
--------------------------------------------------------------------------------
1 | truncate();
10 |
11 | $this->faker = \Faker\Factory::create();
12 |
13 | $numberToCreate = \Config::get('laravel-blog::seed.number');
14 |
15 | for ($i = 0; $i < $numberToCreate; $i++)
16 | {
17 | $this->create();
18 | }
19 |
20 | echo 'Database seeded' . PHP_EOL;
21 | }
22 |
23 | protected function truncate()
24 | {
25 | $replace = \Config::get('laravel-blog::seed.replace');
26 | if ($replace)
27 | {
28 | \DB::table('fbf_blog_posts')->delete();
29 | }
30 | }
31 |
32 | protected function create()
33 | {
34 | $this->post = new Post();
35 | $this->setTitle();
36 | $this->setMedia();
37 | $this->setSummary();
38 | $this->setContent();
39 | $this->setLink();
40 | $this->setIsSticky();
41 | $this->setInRss();
42 | $this->setPageTitle();
43 | $this->setMetaDescription();
44 | $this->setMetaKeywords();
45 | $this->setStatus();
46 | $this->setPublishedDate();
47 | $this->post->save();
48 | }
49 |
50 | protected function setTitle()
51 | {
52 | $title = $this->faker->sentence(rand(1, 10));
53 | $this->post->title = $title;
54 | }
55 |
56 | protected function setMedia()
57 | {
58 | if ($this->hasYouTubeVideos())
59 | {
60 | $this->setYouTubeVideoId();
61 | }
62 | elseif ($this->hasMainImage())
63 | {
64 | $this->doMainImage();
65 | }
66 | }
67 |
68 | protected function hasYouTubeVideos()
69 | {
70 | $youTubeVideoFreq = \Config::get('laravel-blog::seed.you_tube.freq');
71 | $hasYouTubeVideos = $youTubeVideoFreq > 0 && rand(1, $youTubeVideoFreq) == $youTubeVideoFreq;
72 | return $hasYouTubeVideos;
73 | }
74 |
75 | protected function setYouTubeVideoId()
76 | {
77 | $this->post->you_tube_video_id = $this->faker->randomElement(\Config::get('laravel-blog::seed.you_tube.video_ids'));
78 | }
79 |
80 | protected function hasMainImage()
81 | {
82 | $mainImageFreq = \Config::get('laravel-blog::seed.images.main_image.freq');
83 | $hasMainImage = $mainImageFreq > 0 && rand(1, $mainImageFreq) == $mainImageFreq;
84 | return $hasMainImage;
85 | }
86 |
87 | protected function doMainImage()
88 | {
89 | $imageOptions = \Config::get('laravel-blog::images.main_image');
90 | if (!$imageOptions['show'])
91 | {
92 | return false;
93 | }
94 | $seedOptions = \Config::get('laravel-blog::seed.images.main_image');
95 | $original = $this->faker->image(
96 | public_path($imageOptions['original']['dir']),
97 | $seedOptions['original_width'],
98 | $seedOptions['original_height'],
99 | $seedOptions['category']
100 | );
101 | $filename = basename($original);
102 | foreach ($imageOptions['sizes'] as $sizeOptions)
103 | {
104 | $image = $this->faker->image(
105 | public_path($sizeOptions['dir']),
106 | $sizeOptions['width'],
107 | $sizeOptions['height']
108 | );
109 | rename($image, public_path($sizeOptions['dir']) . $filename);
110 | }
111 | $this->post->main_image = $filename;
112 | $this->post->main_image_alt = $this->post->title;
113 | }
114 |
115 | protected function setSummary()
116 | {
117 | $this->post->summary = ''.implode('
', $this->faker->paragraphs(rand(1, 2))).'
';
118 | }
119 |
120 | protected function setContent()
121 | {
122 | $this->post->content = ''.implode('
', $this->faker->paragraphs(rand(4, 10))).'
';
123 | }
124 |
125 | protected function setLink()
126 | {
127 | if ($this->hasLink())
128 | {
129 | $this->setLinkText();
130 | $this->setLinkUrl();
131 | }
132 | }
133 |
134 | protected function hasLink()
135 | {
136 | $showLink = \Config::get('laravel-blog::link.show');
137 | if (!$showLink)
138 | {
139 | return false;
140 | }
141 | $linkFreq = \Config::get('laravel-blog::seed.link.freq');
142 | $hasLink = $linkFreq > 0 && rand(1, $linkFreq) == $linkFreq;
143 | return $hasLink;
144 | }
145 |
146 | protected function setLinkText()
147 | {
148 | $linkTexts = \Config::get('laravel-blog::seed.link.texts');
149 | $this->post->link_text = $this->faker->randomElement($linkTexts);
150 | }
151 |
152 | protected function setLinkUrl()
153 | {
154 | $linkUrls = \Config::get('laravel-blog::seed.link.urls');
155 | $this->post->link_url = $this->faker->randomElement($linkUrls);
156 | }
157 |
158 | protected function setIsSticky()
159 | {
160 | $this->post->is_sticky = (bool) rand(0, 1);
161 | }
162 |
163 | protected function setInRss()
164 | {
165 | $this->post->in_rss = (bool) rand(0, 1);
166 | }
167 |
168 | protected function setPageTitle()
169 | {
170 | $this->post->page_title = $this->post->title;
171 | }
172 |
173 | protected function setMetaDescription()
174 | {
175 | $this->post->meta_description = $this->faker->paragraph(rand(1, 2));
176 | }
177 |
178 | protected function setMetaKeywords()
179 | {
180 | $this->post->meta_keywords = $this->faker->words(10, true);
181 | }
182 |
183 | protected function setStatus()
184 | {
185 | $statuses = array(
186 | Post::DRAFT,
187 | Post::APPROVED
188 | );
189 | $this->post->status = $this->faker->randomElement($statuses);
190 | }
191 |
192 | protected function setPublishedDate()
193 | {
194 | $this->post->published_date = $this->faker->dateTimeBetween('-2 years', '+1 month')->format('Y-m-d H:i:s');
195 | }
196 | }
--------------------------------------------------------------------------------
/src/views/partials/adjacent.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/partials/archives.blade.php:
--------------------------------------------------------------------------------
1 | @if (isset($archives) && !empty($archives))
2 |
3 | @foreach ($archives as $year => $months)
4 | - {{ $year }}
5 |
14 |
15 | @endforeach
16 |
17 | @endif
18 |
--------------------------------------------------------------------------------
/src/views/partials/details.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ trans('laravel-blog::messages.details.all_link_text') }}
6 |
7 |
8 |
9 |
10 | {{ $post->title }}
11 |
12 |
13 |
14 | {{ $post->getDate() }}
15 |
16 |
17 |
18 | {{ $post->summary }}
19 |
20 |
21 | @if (Config::get('laravel-blog::views.view_page.show_share_partial'))
22 | @include('laravel-blog::partials.share')
23 | @endif
24 |
25 | @if (!empty($post->you_tube_video_id))
26 |
27 | {{ $post->getYouTubeEmbedCode() }}
28 |
29 | @elseif (!empty($post->main_image))
30 |
31 | {{ $post->getImage('main_image', 'resized') }}
32 |
33 | @endif
34 |
35 | {{ $post->content }}
36 |
37 | @if (Config::get('laravel-blog::link.show') && !empty($post->link_url) && !empty($post->link_text))
38 |
39 |
40 | {{ $post->link_text }}
41 |
42 |
43 | @endif
44 |
45 |
46 |
47 | @if (Config::get('laravel-blog::views.view_page.show_adjacent_items') && ($newer || $older))
48 | @include('laravel-blog::partials.adjacent')
49 | @endif
--------------------------------------------------------------------------------
/src/views/partials/list.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | @if (!$posts->isEmpty())
4 |
5 | @foreach ($posts as $post)
6 |
7 |
8 |
9 |
14 |
15 |
16 | {{ $post->getDate() }}
17 |
18 |
19 | @if (!empty($post->you_tube_video_id))
20 |
25 | @elseif (!empty($post->main_image))
26 |
31 | @endif
32 |
33 |
34 | {{ $post->summary }}
35 |
36 |
37 |
38 |
39 | {{ trans('laravel-blog::messages.list.more_link_text') }}
40 |
41 |
42 |
43 |
44 |
45 | @endforeach
46 |
47 | {{ $posts->links() }}
48 |
49 | @else
50 |
51 |
52 | {{ trans('laravel-blog::messages.list.no_items') }}
53 |
54 |
55 | @endif
56 |
57 |
--------------------------------------------------------------------------------
/src/views/partials/share.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/posts/index.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.master')
2 |
3 | @section('title')
4 | {{ Config::get('laravel-blog::meta.index_page.page_title') }}
5 | @endsection
6 |
7 | @section('meta_description')
8 | {{ Config::get('laravel-blog::meta.index_page.meta_description') }}
9 | @endsection
10 |
11 | @section('meta_keywords')
12 | {{ Config::get('laravel-blog::meta.index_page.meta_keywords') }}
13 | @endsection
14 |
15 | @section('content')
16 | @include('laravel-blog::partials.list')
17 | @include('laravel-blog::partials.archives')
18 | @stop
--------------------------------------------------------------------------------
/src/views/posts/view.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.master')
2 |
3 | @section('title')
4 | {{ !empty($post->page_title) ? $post->page_title : 'Blog · '.$post->title }}
5 | @endsection
6 |
7 | @section('meta_description')
8 | {{ $post->meta_description }}
9 | @endsection
10 |
11 | @section('meta_keywords')
12 | {{ $post->meta_keywords }}
13 | @endsection
14 |
15 | @section('content')
16 | @include('laravel-blog::partials.details')
17 | @include('laravel-blog::partials.archives')
18 | @stop
19 |
--------------------------------------------------------------------------------