私たちの学校に通知システムを実装したかったのですが、これは一般公開されていないphp/mysql webappなので、あまりトラフィックを受け取りません。 「毎日500〜1000人の訪問者」。
1。私の最初のアプローチはMYSQLトリガーを使用することでした:
Mysql AFTER INSERT trigger
を使用して、notifications
という名前のテーブルにレコードを追加しました。何かのようなもの。
'CREATE TRIGGER `notify_new_homwork` AFTER INSERT ON `homeworks`
FOR EACH ROW INSERT INTO `notifications`
( `from_id`, `note`, `class_id`)
VALUES
(new.user_id,
concat('A New homework Titled: "',left(new.title,'50'),
'".. was added' )
,new.subject_id , 11);'
この種の黒魔術は非常にうまく機能しましたが、この通知が「ユーザーに新しい通知の数を表示する」ために新しいものかどうかを追跡できませんでした。そのため、notificationsという名前のページを追加しました。
通知は次のようなもので取得されます
SELECT n.* from notifications n
JOIN user_class on user_class.class_id = n.class_id where user_class.user_id = X;
注:テーブルuser_classは、ユーザーをクラス「user_id、class_id、subject_id」にリンクします。ユーザーが教師でない限り、subjectはnullです。
今、私の次の課題は。
たとえば、2人のユーザーが何かにコメントした場合、新しい行を挿入せず、「userxと1人がhwにコメントした」などのように古い行を更新するだけです。
どうもありがとう
編集
以下の回答に従って、行に読み取り/未読フラグを設定するには、クラス全体の行だけでなく、各学生の行が必要になります。つまり、トリガーを次のように編集することを意味します。
insert into notifications (from_id,note,student_id,isread)
select new.user_id,new.note,user_id,'0' from user_class where user_class.class_id = new.class_id group by user_class.user_id
まあ、この質問は9ヶ月ですので、OPがまだ答えを必要としているかどうかはわかりませんが、多くの意見とおいしい賞金のために、マスタードも追加したいと思います(ドイツ語)。
この投稿では、通知システムの構築を開始する方法について簡単に説明した例を作成しようとします。
編集:まあまあ、これは予想以上に長い道のりでした。本当に疲れました。ごめんなさい。
質問1:すべての通知にフラグがあります。
質問2:引き続き、すべての通知をデータベース内の単一のレコードとして保存し、要求時にグループ化します。
通知は次のようになります。
+---------------------------------------------+
| ▣ James has uploaded new Homework: Math 1+1 |
+---------------------------------------------+
| ▣ Jane and John liked your comment: Im s... |
+---------------------------------------------+
| ▢ The School is closed on independence day. |
+---------------------------------------------+
カーテンの後ろでは、これは次のようになります。
+--------+-----------+--------+-----------------+-------------------------------------------+
| unread | recipient | sender | type | reference |
+--------+-----------+--------+-----------------+-------------------------------------------+
| true | me | James | homework.create | Math 1 + 1 |
+--------+-----------+--------+-----------------+-------------------------------------------+
| true | me | Jane | comment.like | Im sick of school |
+--------+-----------+--------+-----------------+-------------------------------------------+
| true | me | John | comment.like | Im sick of school |
+--------+-----------+--------+-----------------+-------------------------------------------+
| false | me | system | message | The School is closed on independence day. |
+--------+-----------+--------+-----------------+-------------------------------------------+
注:データベース内で通知をグループ化することはお勧めしません。実行時にこれを行うと、より柔軟になります。
私が取り組んでいるすべてのシステムには、シンプルな1対1通知の参照関係がありました。1対nを覚えておいてください1:1で例を続けます。これは、通知タイプによって定義されるため、参照されるオブジェクトのタイプを定義するフィールドが不要であることも意味します。
SQLの実際のテーブル構造を定義するとき、データベース設計の観点からいくつかの決定を下します。私はこのように見える最も簡単な解決策に行きます:
+--------------+--------+---------------------------------------------------------+
| column | type | description |
+--------------+--------+---------------------------------------------------------+
| id | int | Primary key |
+--------------+--------+---------------------------------------------------------+
| recipient_id | int | The receivers user id. |
+--------------+--------+---------------------------------------------------------+
| sender_id | int | The sender's user id. |
+--------------+--------+---------------------------------------------------------+
| unread | bool | Flag if the recipient has already read the notification |
+--------------+--------+---------------------------------------------------------+
| type | string | The notification type. |
+--------------+--------+---------------------------------------------------------+
| parameters | array | Additional data to render different notification types. |
+--------------+--------+---------------------------------------------------------+
| reference_id | int | The primary key of the referencing object. |
+--------------+--------+---------------------------------------------------------+
| created_at | int | Timestamp of the notification creation date. |
+--------------+--------+---------------------------------------------------------+
または、怠け者の場合、この例ではSQL create tableコマンド:
CREATE TABLE `notifications` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`recipient_id` int(11) NOT NULL,
`sender_id` int(11) NOT NULL,
`unread` tinyint(1) NOT NULL DEFAULT '1',
`type` varchar(255) NOT NULL DEFAULT '',
`parameters` text NOT NULL,
`reference_id` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
この実装は、アプリケーションのニーズに完全に依存します。注:これは、PHPで通知システムを構築する方法に関する黄金の標準ではありません。
これは、通知自体の基本モデルの例であり、必要なプロパティと抽象メソッドmessageForNotification
およびmessageForNotifications
だけを空想したものではありません。
abstract class Notification
{
protected $recipient;
protected $sender;
protected $unread;
protected $type;
protected $parameters;
protected $referenceId;
protected $createdAt;
/**
* Message generators that have to be defined in subclasses
*/
public function messageForNotification(Notification $notification) : string;
public function messageForNotifications(array $notifications) : string;
/**
* Generate message of the current notification.
*/
public function message() : string
{
return $this->messageForNotification($this);
}
}
constructor、getters、settersおよびその種類を追加する必要がありますあなた自身のスタイルで自分のものを、私はすぐに使用できる通知システムを提供するつもりはありません。
これで、タイプごとに新しいNotification
サブクラスを作成できます。次の例は、コメントのlikeアクションを処理します。
実装例:
namespace Notification\Comment;
class CommentLikedNotification extends \Notification
{
/**
* Generate a message for a single notification
*
* @param Notification $notification
* @return string
*/
public function messageForNotification(Notification $notification) : string
{
return $this->sender->getName() . 'has liked your comment: ' . substr($this->reference->text, 0, 10) . '...';
}
/**
* Generate a message for a multiple notifications
*
* @param array $notifications
* @return string
*/
public function messageForNotifications(array $notifications, int $realCount = 0) : string
{
if ($realCount === 0) {
$realCount = count($notifications);
}
// when there are two
if ($realCount === 2) {
$names = $this->messageForTwoNotifications($notifications);
}
// less than five
elseif ($realCount < 5) {
$names = $this->messageForManyNotifications($notifications);
}
// to many
else {
$names = $this->messageForManyManyNotifications($notifications, $realCount);
}
return $names . ' liked your comment: ' . substr($this->reference->text, 0, 10) . '...';
}
/**
* Generate a message for two notifications
*
* John and Jane has liked your comment.
*
* @param array $notifications
* @return string
*/
protected function messageForTwoNotifications(array $notifications) : string
{
list($first, $second) = $notifications;
return $first->getName() . ' and ' . $second->getName(); // John and Jane
}
/**
* Generate a message many notifications
*
* Jane, Johnny, James and Jenny has liked your comment.
*
* @param array $notifications
* @return string
*/
protected function messageForManyNotifications(array $notifications) : string
{
$last = array_pop($notifications);
foreach($notifications as $notification) {
$names .= $notification->getName() . ', ';
}
return substr($names, 0, -2) . ' and ' . $last->getName(); // Jane, Johnny, James and Jenny
}
/**
* Generate a message for many many notifications
*
* Jonny, James and 12 other have liked your comment.
*
* @param array $notifications
* @return string
*/
protected function messageForManyManyNotifications(array $notifications, int $realCount) : string
{
list($first, $second) = array_slice($notifications, 0, 2);
return $first->getName() . ', ' . $second->getName() . ' and ' . $realCount . ' others'; // Jonny, James and 12 other
}
}
アプリケーション内で通知を処理するには、通知マネージャーのようなものを作成します。
class NotificationManager
{
protected $notificationAdapter;
public function add(Notification $notification);
public function markRead(array $notifications);
public function get(User $user, $limit = 20, $offset = 0) : array;
}
このmysqlの例の場合、notificationAdapter
プロパティには、データバックエンドと直接通信するロジックが含まれている必要があります。
間違った解決策がないため、mysql
トリガーの使用は間違っていません。 機能する、機能する..しかし、データベースにアプリケーションロジックを処理させないことを強くお勧めします。
そのため、通知マネージャー内で次のような操作を実行できます。
public function add(Notification $notification)
{
// only save the notification if no possible duplicate is found.
if (!$this->notificationAdapter->isDoublicate($notification))
{
$this->notificationAdapter->add([
'recipient_id' => $notification->recipient->getId(),
'sender_id' => $notification->sender->getId()
'unread' => 1,
'type' => $notification->type,
'parameters' => $notification->parameters,
'reference_id' => $notification->reference->getId(),
'created_at' => time(),
]);
}
}
add
のnotificationAdapter
メソッドの背後には、未加工のmysql挿入コマンドがあります。このアダプターの抽象化を使用すると、mysqlからmongodbのようなドキュメントベースのデータベースに簡単に切り替えることができ、これは通知システムにとって意味があります。
isDoublicate
のnotificationAdapter
メソッドは、同じrecipient
、sender
、type
、およびreference
の通知が既に存在するかどうかを単純に確認する必要があります。
これが単なる例にすぎないことを十分に指摘することはできません。(また、この投稿が途方もなく長くなる次のステップを短くしなければなりません-.-)
したがって、教師が宿題をアップロードするときにアクションを実行する何らかのコントローラーがあると仮定します。
function uploadHomeworkAction(Request $request)
{
// handle the homework and have it stored in the var $homework.
// how you handle your services is up to you...
$notificationManager = new NotificationManager;
foreach($homework->teacher->students as $student)
{
$notification = new Notification\Homework\HomeworkUploadedNotification;
$notification->sender = $homework->teacher;
$notification->recipient = $student;
$notification->reference = $homework;
// send the notification
$notificationManager->add($notification);
}
}
すべての教師の生徒が新しい宿題をアップロードしたときに通知を作成します。
ここで難しい部分があります。 PHP側のグループ化の問題は、現在のユーザーの通知を正しくグループ化するためにallをロードする必要があることです。これは悪いことです。もし少数のユーザーしかいなければ、おそらく問題はないでしょうが、それは良いことではありません。
簡単な解決策は、要求された通知の数を単純に制限し、これらの通知のみをグループ化することです。これは、同様の通知があまり多くない場合(20ごとに3-4など)に正常に機能します。しかし、ユーザー/学生の投稿には約100のいいね!があり、最後の20件の通知のみを選択するとしましょう。ユーザーは、20人が自分の投稿を気に入っていることだけを確認し、それが彼の唯一の通知にもなります。
「正しい」解決策は、すでにデータベースにある通知をグループ化し、通知グループごとにいくつかのサンプルのみを選択することです。それよりも、通知メッセージに実際のカウントを挿入する必要があります。
おそらく以下のテキストを読んでいないので、スニペットを続けましょう。
select *, count(*) as count from notifications
where recipient_id = 1
group by `type`, `reference_id`
order by created_at desc, unread desc
limit 20
これで、特定のユーザーに通知する必要がある通知と、グループに含まれる通知の数がわかりました。
そして今、くだらない部分。グループごとにクエリを実行しないと、グループごとに限られた数の通知を選択するより良い方法を見つけることができませんでした。 ここでの提案はすべて大歓迎です。
だから私は次のようなことをします:
$notifcationGroups = [];
foreach($results as $notification)
{
$notifcationGroup = ['count' => $notification['count']];
// when the group only contains one item we don't
// have to select it's children
if ($notification['count'] == 1)
{
$notifcationGroup['items'] = [$notification];
}
else
{
// example with query builder
$notifcationGroup['items'] = $this->select('notifications')
->where('recipient_id', $recipient_id)
->andWehere('type', $notification['type'])
->andWhere('reference_id', $notification['reference_id'])
->limit(5);
}
$notifcationGroups[] = $notifcationGroup;
}
notificationAdapter
s get
メソッドがこのグループ化を実装し、次のような配列を返すと仮定します。
[
{
count: 12,
items: [Note1, Note2, Note3, Note4, Note5]
},
{
count: 1,
items: [Note1]
},
{
count: 3,
items: [Note1, Note2, Note3]
}
]
グループには常に少なくとも1つの通知があり、順序はUnreadおよびNew通知を優先するため、最初の通知のみを使用できますレンダリングのサンプルとして。
したがって、これらのグループ化された通知を使用するには、新しいオブジェクトが必要です。
class NotificationGroup
{
protected $notifications;
protected $realCount;
public function __construct(array $notifications, int $count)
{
$this->notifications = $notifications;
$this->realCount = $count;
}
public function message()
{
return $this->notifications[0]->messageForNotifications($this->notifications, $this->realCount);
}
// forward all other calls to the first notification
public function __call($method, $arguments)
{
return call_user_func_array([$this->notifications[0], $method], $arguments);
}
}
そして最後に、ほとんどのものを実際にまとめることができます。 NotificationManager
のget関数は次のようになります。
public function get(User $user, $limit = 20, $offset = 0) : array
{
$groups = [];
foreach($this->notificationAdapter->get($user->getId(), $limit, $offset) as $group)
{
$groups[] = new NotificationGroup($group['notifications'], $group['count']);
}
return $gorups;
}
そして、実際に可能なコントローラーアクションの内部:
public function viewNotificationsAction(Request $request)
{
$notificationManager = new NotificationManager;
foreach($notifications = $notificationManager->get($this->getUser()) as $group)
{
echo $group->unread . ' | ' . $group->message() . ' - ' . $group->createdAt() . "\n";
}
// mark them as read
$notificationManager->markRead($notifications);
}
回答:
通知に読み取り/未読変数を導入します。その後、SQLで... WHERE status = 'UNREAD'を実行することで、未読の通知のみをプルできます。
あなたは本当にできない...あなたはその通知をプッシュしたくなるでしょう。できることは、GROUP BYを使用してそれらを集約することです。新しい宿題のようなユニークなものでグループ化したいと思うかもしれません。それは... GROUP BY homework
.id
ユーザーのIDとユーザーが既読としてマークしたい通知のIDを含むNotificationsReadテーブルを作成することで問題を解決できます。 (この方法により、各生徒と教師を分離したままにすることができます。)その後、そのテーブルを通知のテーブルに参加させることができます。
geggletoの答えは2番目の部分について正しかったので、SELECT *, COUNT(*) AS counter WHERE ... GROUP BY 'type'
で通知を取得できます。その後、同じタイプがいくつあるかがわかり、「userxと1つのその他のhwのコメント」を表示できます.
また、表示したいテキスト全体を保存せず、代わりに必要な情報を保存することをお勧めします:from_id、class_id、type、nameなど-この方法では、必要に応じて後で簡単にメカニズムを変更できます。より少なく保管する必要があります。