├── CHANGELOG.md ├── start.php ├── config └── sluggable.php ├── sluggable.php └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ### Version 1.0 -- 12-Sep-2012 5 | 6 | - initial release 7 | - global configuration is done in `application/config/sluggable.php` and 8 | model-level configuration is done in model 9 | 10 | -------------------------------------------------------------------------------- /start.php: -------------------------------------------------------------------------------- 1 | 9 | * @link http://github.com/cviebrock/sluggable 10 | */ 11 | 12 | 13 | Autoloader::map(array( 14 | 'Sluggable' => __DIR__ . DS . 'sluggable.php', 15 | )); 16 | 17 | 18 | // Listen to the Eloquent save event so we can do our thing: 19 | 20 | Event::listen('eloquent.saving', array('Sluggable','make') ); 21 | -------------------------------------------------------------------------------- /config/sluggable.php: -------------------------------------------------------------------------------- 1 | 9 | * @link http://github.com/cviebrock/sluggable 10 | */ 11 | 12 | 13 | return array( 14 | 15 | /** 16 | * What attributes do we use to build the slug? 17 | * This can be a single field, like "name" which will build a slug from: 18 | * 19 | * $model->name; 20 | * 21 | * Or it can be an array of fields, like ("name", "company"), which builds a slug from: 22 | * 23 | * $model->name . ' ' . $model->company; 24 | * 25 | * If you've defined custom getters in your model, you can use those too, 26 | * since Eloquent will call them when you request a custom attribute. 27 | * 28 | * Defaults to null, which uses the toString() method on your model. 29 | * 30 | */ 31 | 'build_from' => null, 32 | 33 | /** 34 | * What field to we store the slug in? Defaults to "slug". 35 | * You need to configure this when building the SQL for your database, e.g.: 36 | * 37 | * Schema::create('users', function($table) 38 | * { 39 | * $table->string('slug'); 40 | * }); 41 | * 42 | */ 43 | 'save_to' => 'slug', 44 | 45 | /** 46 | * What style should we use to build the slug? 47 | * 48 | * - "slug" uses Laravel's Str::slug() 49 | * 50 | * Defaults to "slug" (only option for now). 51 | */ 52 | 'style' => 'slug', 53 | 54 | /** 55 | * Separator to use. Defaults to a hyphen. 56 | */ 57 | 'separator' => '-', 58 | 59 | /** 60 | * Enforce uniqueness of slugs? Defaults to true. 61 | * If a generated slug already exists, an incremental numeric 62 | * value will be appended to the end until a unique slug is found. e.g.: 63 | * 64 | * my-slug 65 | * my-slug-1 66 | * my-slug-2 67 | */ 68 | 'unique' => true, 69 | 70 | /** 71 | * Whether to update the slug value when a model is being 72 | * re-saved (i.e. already exists). Defaults to false, which 73 | * means slugs are not updated. 74 | */ 75 | 'on_update' => false, 76 | 77 | ); 78 | -------------------------------------------------------------------------------- /sluggable.php: -------------------------------------------------------------------------------- 1 | 9 | * @link http://github.com/cviebrock/sluggable 10 | */ 11 | 12 | 13 | class Sluggable { 14 | 15 | 16 | 17 | /** 18 | * Method that gets fired when the eloquent model is saved. 19 | * Handles the slugging. 20 | * 21 | * @param Model $model 22 | * @return bool 23 | */ 24 | public static function make( $model, $force = false ) 25 | { 26 | 27 | 28 | // skip if the model isn't sluggable 29 | if ( !isset( $model::$sluggable ) ) { 30 | return true; 31 | } 32 | 33 | $model_config = $model::$sluggable; 34 | 35 | 36 | // read the default config: 37 | // 1. application/config/sluggable.php 38 | // 2. bundles/sluggable/config/sluggable.php 39 | 40 | $default_config = Config::get('sluggable', Config::get('sluggable::sluggable', array() )); 41 | 42 | $config = array_merge( $default_config, $model_config ); 43 | 44 | 45 | // nicer variables for readability 46 | 47 | $build_from = $save_to = $style = $separator = $unique = $on_update = null; 48 | extract( $config, EXTR_IF_EXISTS ); 49 | 50 | 51 | // skip slug generation if the model exists or the slug field is already populated, 52 | // and on_update is false ... unless we are forcing things! 53 | 54 | if (!$force) { 55 | if ( ( $model->exists || !empty($model->{$save_to}) ) && !$on_update ) { 56 | return true; 57 | } 58 | } 59 | 60 | 61 | // build the slug string 62 | 63 | if ( $build_from ) { 64 | 65 | if ( !is_array( $build_from ) ) { 66 | 67 | $build_from = array( $build_from ); 68 | 69 | } 70 | 71 | $string = ''; 72 | foreach( $build_from as $field ) { 73 | $string .= $model->{$field} . ' '; 74 | } 75 | 76 | } else { 77 | 78 | $string = $model->__toString(); 79 | } 80 | 81 | $string = trim( $string ); 82 | 83 | 84 | // build slug using given slug style 85 | 86 | switch ($config['style']) { 87 | case 'slug': 88 | default: 89 | $slug = Str::slug( $string, $config['separator'] ); 90 | break; 91 | } 92 | 93 | // check for uniqueness? 94 | 95 | if ( $unique ) { 96 | 97 | $class = get_class($model); 98 | 99 | $last = $class::where( $save_to, 'LIKE', $slug.'%' ) 100 | ->order_by( $save_to, 'DESC' ) 101 | ->first(); 102 | 103 | if ( $last ) { 104 | 105 | $idx = substr( $last->{$save_to} , strlen($slug) ); 106 | $idx = ltrim( $idx, $separator ); 107 | $idx = intval( $idx ); 108 | $idx++; 109 | 110 | $slug .= $separator . $idx; 111 | 112 | } 113 | 114 | } 115 | 116 | 117 | // update the slug field 118 | 119 | $model->{$save_to} = $slug; 120 | 121 | // done! 122 | 123 | return true; 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sluggable 2 | 3 | Easy automatic slug generation for your Eloquent models. 4 | 5 | > ### NOTE 6 | > This bundle is designed for Laravel 3. 7 | > If you are running Laravel 4, then you should be using the L4 package: 8 | > __cviebrock/eloquent-sluggable__ 9 | > 10 | > - [Github link](https://github.com/cviebrock/eloquent-sluggable) 11 | > - [Packagist link](https://packagist.org/packages/cviebrock/eloquent-sluggable) 12 | > 13 | > Configuration is almost identical, so migrating from L3 to L4 shouldn't be a pain! 14 | 15 | 16 | 17 | ## Installing the Bundle 18 | 19 | Install the bundle using Artisan: 20 | 21 | ``` 22 | php artisan bundle::install sluggable 23 | ``` 24 | 25 | Update your `application/bundles.php` file with: 26 | 27 | ```php 28 | 'sluggable' => array( 'auto' => true ), 29 | ``` 30 | 31 | ## Updating your Models 32 | 33 | Define a public static property `$sluggable` with the definitions 34 | (see [#Configuration] below for details): 35 | 36 | ```php 37 | class Post extends Eloquent 38 | { 39 | 40 | public static $sluggable = array( 41 | 'build_from' => 'title', 42 | 'save_to' => 'slug', 43 | ); 44 | 45 | } 46 | ``` 47 | 48 | That's it ... your model is now "sluggable"! 49 | 50 | 51 | ## Using the Class 52 | 53 | Saving a model is easy: 54 | 55 | ```php 56 | $post = new Post(array( 57 | 'title' => 'My Awesome Blog Post' 58 | )); 59 | 60 | $post->save(); 61 | ``` 62 | 63 | And so is retrieving the slug: 64 | 65 | ```php 66 | echo $post->slug; 67 | ``` 68 | 69 | 70 | 71 | ## Configuration 72 | 73 | Configuration was designed to be as flexible as possible. You can set up 74 | defaults for all of your Eloquent models, and then override those settings 75 | for individual models. 76 | 77 | By default, global configuration can be set in the 78 | `application/config/sluggable.php` file. If a configuration isn't set, 79 | then the bundle defaults from `bundles/sluggable/config/sluggable.php` 80 | are used. Here is an example configuration, with all the settings shown: 81 | 82 | ```php 83 | return array( 84 | 'build_from' => null, 85 | 'save_to' => 'slug', 86 | 'style' => 'slug', 87 | 'separator' => '-', 88 | 'unique' => true, 89 | 'on_update' => false, 90 | ); 91 | ``` 92 | 93 | `build_from` is the field or array of fields from which to build the slug. 94 | Each `$model->field` is contactenated (with space separation) to build the 95 | sluggable string. This can be model attribues (i.e. fields in the database) 96 | or custom getters. So, for example, this works: 97 | 98 | ```php 99 | class Person extends Eloquent { 100 | 101 | public static $sluggable = array( 102 | 'build_from' => 'fullname' 103 | ); 104 | 105 | public function get_fullname() { 106 | return $this->firstname . ' ' . $this->lastname; 107 | } 108 | 109 | } 110 | ``` 111 | 112 | If `build_from` is empty, false or null, then the value of `$model->__toString()` 113 | is used. 114 | 115 | `save_to` is the field in your model where the slug is stored. By default, 116 | this is "slug". You need to create this column in your table when defining 117 | your schema: 118 | 119 | ```php 120 | Schema::create('posts', function($table) 121 | { 122 | $table->increments('id'); 123 | $table->string('title'); 124 | $table->string('body'); 125 | $table->string('slug'); 126 | $table->timestamps(); 127 | }); 128 | ``` 129 | 130 | `style` defines the method used to turn the sluggable string into a slug. 131 | Right now (version 1.0) the only option is "slug" which uses Laravel's 132 | `Str::slug()` method. 133 | 134 | `separator` defines the separator used when building a slug. Default is a 135 | hyphen. 136 | 137 | `unique` is a boolean defining whether slugs should be unique among all 138 | models of the given type. For example, if you have two blog posts and both 139 | are called "My Blog Post", then they will both sluggify to "my-blog-post" 140 | (when using Sluggable's default settings). This could be a problem, e.g. if you 141 | use the slug in URLs. 142 | 143 | By turning `unique` on, then the second Post model will sluggify to 144 | "my-blog-post-1". If there is a third post with the same title, it will 145 | sluggify to "my-blog-post-2" and so on. Each subsequent model will get 146 | an incremental value appended to the end of the slug, ensuring uniqueness. 147 | 148 | `on_update` is a boolean. If it is `false` (the default value), then slugs 149 | will not be updated if a model is resaved (e.g. if you change the title 150 | of your blog post, the slug will remain the same) or the slug value has already 151 | been set. You can set it to `true` (or manually change the $model->slug value 152 | in your own code) if you want to override this behaviour. 153 | 154 | (If you want to manually set the slug value using your model's Sluggable settings, 155 | you can run `Sluggable::make($model, true)`. The second arguement forces 156 | Sluggable to update the slug field.) 157 | 158 | 159 | ## Credits 160 | 161 | The idea for this bundle came from using `actAs Sluggable` from the Doctrine ORM. 162 | --------------------------------------------------------------------------------