├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src
├── Tricki
│ └── Notification
│ │ ├── Facade.php
│ │ ├── Models
│ │ ├── AbstractEloquent.php
│ │ ├── Notification.php
│ │ └── NotificationUser.php
│ │ ├── Notification.php
│ │ └── NotificationServiceProvider.php
├── config
│ ├── .gitkeep
│ └── config.php
└── migrations
│ ├── .gitkeep
│ ├── 2015_01_24_124832_create_notifications_tables.php
│ └── 2015_02_08_132023_make_read_at_nullable.php
└── tests
└── .gitkeep
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - hhvm
8 |
9 | before_script:
10 | - travis_retry composer self-update
11 | - travis_retry composer install --prefer-source --no-interaction --dev
12 |
13 | script: phpunit
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Thomas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Laravel 4 Notification
2 | ======
3 |
4 | A basic starting point for a flexible user notification system in Laravel 4.
5 |
6 | It is easily extendable with new notification types and leaves rendering completely up to you.
7 |
8 | This package only provides an extendable notification system without any controllers or views
9 | since they are often very use case specific.
10 |
11 | I'm open to ideas for extending this package.
12 |
13 | ## Installation
14 |
15 | ### 1. Install with Composer
16 |
17 | ```bash
18 | composer require tricki/laravel-notification:@dev
19 | ```
20 |
21 | This will update `composer.json` and install it into the `vendor/` directory.
22 |
23 | (See the [Packagist website](https://packagist.org/packages/tricki/laravel-notification) for a list of available version numbers and
24 | development releases.)
25 |
26 | ### 2. Add to Providers in `config/app.php`
27 |
28 | ```php
29 | 'providers' => [
30 | // ...
31 | 'Tricki\Notification\NotificationServiceProvider',
32 | ],
33 | ```
34 |
35 | This registers the package with Laravel and automatically creates an alias called
36 | `Notification`.
37 |
38 | ### 3. Publishing config
39 |
40 | If your models are namespaced you will have to declare this in the package configuration.
41 |
42 | Publish the package configuration using Artisan:
43 |
44 | ```bash
45 | php artisan config:publish tricki/laravel-notification
46 | ```
47 |
48 | Set the `namespace` property of the newly created `app/config/packages/tricki/laravel-notification/config.php`
49 | to the namespace of your notification models.
50 |
51 | #### Example
52 |
53 | ```php
54 | 'namespace' => '\MyApp\Models\'
55 | ```
56 |
57 | ### 4. Executing migration
58 |
59 | ```bash
60 | php artisan migrate --package="tricki/laravel-notification"
61 | ```
62 |
63 | ### 5. Adding relationship to User
64 |
65 | Extend your User model with the following relationship:
66 |
67 | ```php
68 |
69 | public function notifications()
70 | {
71 | return $this->hasMany('\Tricki\Notification\Models\NotificationUser');
72 | }
73 |
74 | ```
75 |
76 | ## Usage
77 |
78 | ### 1. Define notification models
79 |
80 | You will need separate models for each type of notification. Some examples would
81 | be `PostLikedNotification` or `CommentPostedNotification`.
82 |
83 | These models define the unique behavior of each notification type like it's actions
84 | and rendering.
85 |
86 | A minimal notification model looks like this:
87 |
88 | ```php
89 | Remember to add the namespace of your notification models to this package's `config.php`.
103 |
104 | ### 2. Create a notification
105 |
106 | Notifications can be created using `Notification::create`.
107 |
108 | The function takes 5 parameters:
109 |
110 | * **$type** string
111 | The notification type (see [Define notification models](#1-define-notification-models))
112 | * **$sender** Model
113 | The object that initiated the notification (a user, a group, a web service etc.)
114 | * **$object** Model | NULL
115 | An object that was changed (a post that has been liked).
116 | * **$users** array | Collection | User
117 | The user(s) which should receive this notification.
118 | * **$data** mixed | NULL
119 | Any additional data you want to attach. This will be serialized into the database.
120 |
121 | ### 3. Retrieving a user's notifications
122 |
123 | You can get a collection of notifications sent to a user using the `notifications` relationship,
124 | which will return a collection of your notification models.
125 |
126 | You can easily get a collection of all notifications sent to a user:
127 |
128 | ```php
129 | $user = User::find($id);
130 | $notifications = $user->notifications;
131 | ```
132 |
133 | You can also only get read or unread notifications using the `read` and `unread` scopes respectively:
134 |
135 | ```php
136 | $readNotifications = $user->notifications()->read()->get();
137 | $unreadNotifications = $user->notifications()->unread()->get();
138 | ```
139 |
140 | Since the notifications are instances of your own models you can easily have different behavior or
141 | output for each notification type.
142 |
143 | #### Example:
144 |
145 | ```php
146 |
168 | ```
169 |
170 | ```html
171 | // notifications.blade.php
172 |
173 |
174 | @foreach($user->notifications as $notification)
175 | - {{ $notification->render() }}
176 | @endforeach
177 |
178 | ```
179 |
180 | This could output:
181 | ```html
182 |
183 | - this is a post_liked notification
184 | - this is a comment_posted notification
185 |
186 | ```
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tricki/laravel-notification",
3 | "description": "A basic user notification package for Laravel 4",
4 | "keywords": ["laravel", "notification"],
5 | "homepage": "https://github.com/tricki/Notification",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Thomas Rickenbach",
10 | "email": "thomasrickenbach@gmail.com"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.4.0",
15 | "illuminate/support": "4.2.*"
16 | },
17 | "autoload": {
18 | "classmap": [
19 | "src/migrations"
20 | ],
21 | "psr-0": {
22 | "Tricki\\Notification\\": "src/"
23 | }
24 | },
25 | "minimum-stability": "stable"
26 | }
27 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Tricki/Notification/Facade.php:
--------------------------------------------------------------------------------
1 | isSuperType)
28 | {
29 | return $this->newInstance();
30 | }
31 | else
32 | {
33 | if (!isset($attributes[$this->typeField]))
34 | {
35 | throw new \DomainException($this->typeField . ' not present in the records of a Super Model');
36 | }
37 | else
38 | {
39 | $class = $this->getClass($attributes[$this->typeField]);
40 | return new $class;
41 | }
42 | }
43 | }
44 |
45 | /**
46 | * Create a new model instance requested by the builder.
47 | *
48 | * @param array $attributes
49 | * @return Illuminate\Database\Eloquent\Model
50 | */
51 | public function newFromBuilder($attributes = array())
52 | {
53 | $m = $this->mapData((array) $attributes)->newInstance(array(), true);
54 | $m->setRawAttributes((array) $attributes, true);
55 | return $m;
56 | }
57 |
58 | /**
59 | * Get a new query builder for the model's table.
60 | *
61 | * @return Reposed\Builder
62 | */
63 | public function newRawQuery()
64 | {
65 | $builder = $this->newEloquentBuilder($this->newBaseQueryBuilder());
66 |
67 | // Once we have the query builders, we will set the model instances
68 | // so the builder can easily access any information it may need
69 | // from the model while it is constructing and executing various
70 | // queries against it.
71 | $builder->setModel($this)->with($this->with);
72 | return $builder;
73 | }
74 |
75 | /**
76 | * Get a new query builder for the model.
77 | * set any type of scope you want on this builder in a child class, and it'll
78 | * keep applying the scope on any read-queries on this model
79 | *
80 | * @return Reposed\Builder
81 | */
82 | public function newQuery($excludeDeleted = true)
83 | {
84 | $builder = parent::newQuery($excludeDeleted);
85 |
86 | if ($this->isSubType())
87 | {
88 | $builder->where($this->typeField, $this->getClass($this->typeField));
89 | }
90 |
91 | return $builder;
92 | }
93 |
94 | protected function isSubType()
95 | {
96 | return $this->isSubType;
97 | }
98 |
99 | protected function getClass($type)
100 | {
101 | return get_class($this);
102 | }
103 |
104 | protected function getType()
105 | {
106 | return get_class($this);
107 | }
108 |
109 | /**
110 | * Save the model to the database.
111 | *
112 | * @return bool
113 | */
114 | public function save(array $options = array())
115 | {
116 | if ($this->isSubType())
117 | {
118 | $this->attributes[$this->typeField] = $this->getType();
119 | }
120 |
121 | return parent::save($options);
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/Tricki/Notification/Models/Notification.php:
--------------------------------------------------------------------------------
1 | hasMany('Tricki\Notification\Models\NotificationUser', 'notification_id');
33 | }
34 |
35 | public function newPivot(\Eloquent $parent, array $attributes, $table, $exists)
36 | {
37 | return new NotificationUser($parent, $attributes, $table, $exists);
38 | }
39 |
40 | public function sender()
41 | {
42 | return $this->morphTo();
43 | }
44 |
45 | public function object()
46 | {
47 | return $this->morphTo();
48 | }
49 |
50 | public function scopeUnread($query)
51 | {
52 | return $query->where('read_at', NULL);
53 | }
54 |
55 | public function scopeRead($query)
56 | {
57 | return $query->whereNotNull('read_at');
58 | }
59 |
60 | protected function isSubType()
61 | {
62 | return get_class() !== get_class($this);
63 | }
64 |
65 | protected function getClass($type)
66 | {
67 | return \Notification::getClass($type);
68 | }
69 |
70 | protected function getType()
71 | {
72 | return static::$type;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/Tricki/Notification/Models/NotificationUser.php:
--------------------------------------------------------------------------------
1 | table;
37 | }
38 | parent::__construct($parent, $attributes, $table, $exists);
39 | }
40 |
41 | public static function boot()
42 | {
43 | parent::boot();
44 |
45 | static::created(function($model)
46 | {
47 | $responses = Event::fire('notification::assigned', array($model));
48 | });
49 |
50 | static::saving(function($model)
51 | {
52 | $model->updateTimestamps();
53 | });
54 | }
55 |
56 | public function user()
57 | {
58 | return $this->belongsTo(Config::get('auth.model'));
59 | }
60 |
61 | public function notification()
62 | {
63 | return $this->belongsTo('Tricki\Notification\Models\Notification');
64 | }
65 |
66 | public function scopeUnread($query)
67 | {
68 | return $query->where('notification_user.read_at', NULL);
69 | }
70 |
71 | public function scopeRead($query)
72 | {
73 | return $query->whereNotNull('notification_user.read_at');
74 | }
75 |
76 | /**
77 | * Mark the user notification as read
78 | *
79 | * @return \Tricki\Notification\Models\NotificationUser
80 | */
81 | public function setRead()
82 | {
83 | $this->read_at = new \DateTime();
84 | $this->save();
85 |
86 | return $this;
87 | }
88 |
89 | /**
90 | * Mark the user notification as unread
91 | *
92 | * @return \Tricki\Notification\Models\NotificationUser
93 | */
94 | public function setUnread()
95 | {
96 | $this->read_at = NULL;
97 | $this->save();
98 |
99 | return $this;
100 | }
101 |
102 | public function render()
103 | {
104 | return $this->notification->render();
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/Tricki/Notification/Notification.php:
--------------------------------------------------------------------------------
1 | getClass($type);
43 | $notification = new $class();
44 |
45 | if ($data)
46 | {
47 | $notification->data = $data;
48 | }
49 | $notification->sender()->associate($sender);
50 | if ($object)
51 | {
52 | $notification->object()->associate($object);
53 | }
54 | $notification->save();
55 |
56 | $notification_users = array();
57 | foreach ($users as $user)
58 | {
59 | $notification_user = new Models\NotificationUser($notification);
60 | $notification_user->user_id = $user->id;
61 | $notification_users[] = $notification_user;
62 | }
63 | $notification->users()->saveMany($notification_users);
64 |
65 | return $notification;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/Tricki/Notification/NotificationServiceProvider.php:
--------------------------------------------------------------------------------
1 | package('tricki/laravel-notification');
25 | }
26 |
27 | /**
28 | * Register the service provider.
29 | *
30 | * @return void
31 | */
32 | public function register()
33 | {
34 | $this->app->singleton('notification', function()
35 | {
36 | return new Notification;
37 | });
38 | $this->app->booting(function()
39 | {
40 | $loader = \Illuminate\Foundation\AliasLoader::getInstance();
41 | $loader->alias('Notification', 'Tricki\Notification\Facade');
42 | });
43 | }
44 |
45 | /**
46 | * Get the services provided by the provider.
47 | *
48 | * @return array
49 | */
50 | public function provides()
51 | {
52 | return array('notification');
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/config/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tricki/laravel-notification/b8b72273e01faaf15f91cc528b4643ceaffd4709/src/config/.gitkeep
--------------------------------------------------------------------------------
/src/config/config.php:
--------------------------------------------------------------------------------
1 | ''
5 | );
--------------------------------------------------------------------------------
/src/migrations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tricki/laravel-notification/b8b72273e01faaf15f91cc528b4643ceaffd4709/src/migrations/.gitkeep
--------------------------------------------------------------------------------
/src/migrations/2015_01_24_124832_create_notifications_tables.php:
--------------------------------------------------------------------------------
1 | increments('id');
18 | switch(Config::get('notification::type_format')) {
19 | case 'integer':
20 | $table->integer('type')->unsigned();
21 | break;
22 | default:
23 | case 'class':
24 | $table->string('type');
25 | }
26 | $table->morphs('sender');
27 | $table->morphs('object');
28 | $table->text('data')->nullable();
29 | $table->timestamps();
30 | });
31 |
32 | Schema::create('notification_user', function(Blueprint $table)
33 | {
34 | $table->increments('id');
35 | $table->integer('notification_id');
36 | $table->integer('user_id');
37 | $table->timestamp('read_at');
38 | $table->timestamps();
39 | $table->softDeletes();
40 | });
41 | }
42 |
43 | /**
44 | * Reverse the migrations.
45 | *
46 | * @return void
47 | */
48 | public function down()
49 | {
50 | Schema::drop('notifications');
51 | Schema::drop('notification_user');
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/migrations/2015_02_08_132023_make_read_at_nullable.php:
--------------------------------------------------------------------------------
1 |