Просмотр файла app/Models/User.php

Размер файла: 22.21Kb
  1. <?php
  2.  
  3. declare(strict_types=1);
  4.  
  5. namespace App\Models;
  6.  
  7. use App\Traits\UploadTrait;
  8. use Exception;
  9. use GuzzleHttp\Client;
  10. use GuzzleHttp\Exception\GuzzleException;
  11. use Illuminate\Auth\Authenticatable;
  12. use Illuminate\Auth\MustVerifyEmail;
  13. use Illuminate\Auth\Passwords\CanResetPassword;
  14. use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
  15. use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
  16. use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
  17. use Illuminate\Database\Eloquent\Builder;
  18. use Illuminate\Database\Eloquent\Collection;
  19. use Illuminate\Database\Eloquent\Factories\HasFactory;
  20. use Illuminate\Database\Eloquent\Model;
  21. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  22. use Illuminate\Database\Eloquent\Relations\HasMany;
  23. use Illuminate\Database\Eloquent\Relations\HasOne;
  24. use Illuminate\Database\Query\JoinClause;
  25. use Illuminate\Foundation\Auth\Access\Authorizable;
  26. use Illuminate\Notifications\Notifiable;
  27. use Illuminate\Support\Facades\Cache;
  28. use Illuminate\Support\HtmlString;
  29.  
  30. /**
  31. * Class User
  32. *
  33. * @property int id
  34. * @property string login
  35. * @property string password
  36. * @property string email
  37. * @property string level
  38. * @property string name
  39. * @property string country
  40. * @property string city
  41. * @property string language
  42. * @property string info
  43. * @property string site
  44. * @property string phone
  45. * @property string gender
  46. * @property string birthday
  47. * @property int visits
  48. * @property int newprivat
  49. * @property int newwall
  50. * @property int allforum
  51. * @property int allguest
  52. * @property int allcomments
  53. * @property string themes
  54. * @property string timezone
  55. * @property int point
  56. * @property int money
  57. * @property string status
  58. * @property string color
  59. * @property string avatar
  60. * @property string picture
  61. * @property int rating
  62. * @property int posrating
  63. * @property int negrating
  64. * @property string keypasswd
  65. * @property int timepasswd
  66. * @property int sendprivatmail
  67. * @property int timebonus
  68. * @property string confirmregkey
  69. * @property int newchat
  70. * @property int notify
  71. * @property string apikey
  72. * @property string subscribe
  73. * @property int timeban
  74. * @property int updated_at
  75. * @property int created_at
  76. *
  77. * @property Collection<UserData> data
  78. */
  79. class User extends BaseModel implements
  80. AuthenticatableContract,
  81. AuthorizableContract,
  82. CanResetPasswordContract
  83. {
  84. use Authenticatable;
  85. use Authorizable;
  86. use CanResetPassword;
  87. use MustVerifyEmail;
  88. use HasFactory;
  89. use Notifiable;
  90. use UploadTrait;
  91.  
  92. public const BOSS = 'boss'; // Владелец
  93. public const ADMIN = 'admin'; // Админ
  94. public const MODER = 'moder'; // Модератор
  95. public const EDITOR = 'editor'; // Редактор
  96. public const USER = 'user'; // Пользователь
  97. public const PENDED = 'pended'; // Ожидающий
  98. public const BANNED = 'banned'; // Забаненный
  99.  
  100. /**
  101. * Администраторы
  102. */
  103. public const ADMIN_GROUPS = [
  104. self::BOSS,
  105. self::ADMIN,
  106. self::MODER,
  107. self::EDITOR,
  108. ];
  109.  
  110. /**
  111. * Участники
  112. */
  113. public const USER_GROUPS = [
  114. self::BOSS,
  115. self::ADMIN,
  116. self::MODER,
  117. self::EDITOR,
  118. self::USER,
  119. ];
  120.  
  121. /**
  122. * Все пользователи
  123. */
  124. public const ALL_GROUPS = [
  125. self::BOSS,
  126. self::ADMIN,
  127. self::MODER,
  128. self::EDITOR,
  129. self::USER,
  130. self::PENDED,
  131. self::BANNED,
  132. ];
  133.  
  134. /**
  135. * Genders
  136. */
  137. public const MALE = 'male';
  138. public const FEMALE = 'female';
  139.  
  140. /**
  141. * Indicates if the model should be timestamped.
  142. *
  143. * @var bool
  144. */
  145. public $timestamps = false;
  146.  
  147. /**
  148. * The attributes that aren't mass assignable.
  149. *
  150. * @var array
  151. */
  152. protected $guarded = [];
  153.  
  154. /**
  155. * The attributes that should be hidden for arrays.
  156. *
  157. * @var array
  158. */
  159. protected $hidden = [
  160. 'password',
  161. 'remember_token',
  162. ];
  163.  
  164. /**
  165. * Директория загрузки файлов
  166. *
  167. * @var string
  168. */
  169. public $uploadPath = '/uploads/pictures';
  170.  
  171. /**
  172. * Директория загрузки аватаров
  173. *
  174. * @var string
  175. */
  176. public $uploadAvatarPath = '/uploads/avatars';
  177.  
  178. /**
  179. * Связь с таблицей online
  180. *
  181. * @return BelongsTo
  182. */
  183. public function online(): BelongsTo
  184. {
  185. return $this->belongsTo(Online::class, 'id', 'user_id')->withDefault();
  186. }
  187.  
  188. /**
  189. * Возвращает последний бан
  190. *
  191. * @return hasOne
  192. */
  193. public function lastBan(): hasOne
  194. {
  195. return $this->hasOne(Banhist::class, 'user_id', 'id')
  196. ->whereIn('type', ['ban', 'change'])
  197. ->orderByDesc('created_at')
  198. ->withDefault();
  199. }
  200.  
  201. /**
  202. * Возвращает заметку пользователя
  203. *
  204. * @return hasOne
  205. */
  206. public function note(): HasOne
  207. {
  208. return $this->hasOne(Note::class)->withDefault();
  209. }
  210.  
  211. /**
  212. * Возвращает дополнительные поля
  213. *
  214. * @return HasMany
  215. */
  216. public function data(): HasMany
  217. {
  218. return $this->hasMany(UserData::class, 'user_id');
  219. }
  220.  
  221. /**
  222. * Возвращает имя или логин пользователя
  223. *
  224. * @return string
  225. */
  226. public function getName(): string
  227. {
  228. if ($this->id) {
  229. return $this->name ?: $this->login;
  230. }
  231.  
  232. return setting('deleted_user');
  233. }
  234.  
  235. /**
  236. * Возвращает ссылку на профиль пользователя
  237. *
  238. * @return HtmlString Путь к профилю
  239. */
  240. public function getProfile(): HtmlString
  241. {
  242. if ($this->id) {
  243. $admin = null;
  244. $name = check($this->getName());
  245.  
  246. if ($this->color) {
  247. $name = '<span style="color:' . $this->color . '">' . $name . '</span>';
  248. }
  249.  
  250. if (in_array($this->level, self::ADMIN_GROUPS, true)) {
  251. $admin = ' <i class="fas fa-xs fa-star text-info" title="' . $this->getLevel() . '"></i>';
  252. }
  253.  
  254. $html = '<a class="section-author fw-bold" href="/users/' . $this->login . '" data-login="@' . $this->login . '">' . $name . '</a>';
  255.  
  256. return new HtmlString($html . $admin);
  257. }
  258.  
  259. $html = '<span class="section-author fw-bold" data-login="' . setting('deleted_user') . '">' . setting('deleted_user') . '</span>';
  260.  
  261. return new HtmlString($html);
  262. }
  263.  
  264. /**
  265. * Возвращает пол пользователя
  266. *
  267. * @return HtmlString пол пользователя
  268. */
  269. public function getGender(): HtmlString
  270. {
  271. if ($this->gender === 'female') {
  272. return new HtmlString('<i class="fa fa-female fa-lg"></i>');
  273. }
  274.  
  275. return new HtmlString('<i class="fa fa-male fa-lg"></i>');
  276. }
  277.  
  278. /**
  279. * Авторизует пользователя
  280. *
  281. * @param string $login Логин
  282. * @param string $password Пароль пользователя
  283. * @param bool $remember Запомнить пароль
  284. *
  285. * @return User|bool
  286. */
  287. public static function auth(string $login, string $password, bool $remember = true)
  288. {
  289. if (! empty($login) && ! empty($password)) {
  290. $user = getUserByLoginOrEmail($login);
  291.  
  292. if ($user && password_verify($password, $user->password)) {
  293. (new self())->rememberUser($user, $remember);
  294.  
  295. // Сохранение привязки к соц. сетям
  296. if (session()->has('social')) {
  297. Social::query()->create([
  298. 'user_id' => $user->id,
  299. 'network' => session()->get('social')->network,
  300. 'uid' => session()->get('social')->uid,
  301. 'created_at' => SITETIME,
  302. ]);
  303. }
  304.  
  305. $user->saveVisit(Login::AUTH);
  306.  
  307. return $user;
  308. }
  309. }
  310.  
  311. return false;
  312. }
  313.  
  314. /**
  315. * Авторизует через социальные сети
  316. *
  317. * @param string $token идентификатор Ulogin
  318. *
  319. * @return User|bool
  320. * @throws GuzzleException
  321. */
  322. public static function socialAuth(string $token)
  323. {
  324. $client = new Client(['timeout' => 30.0]);
  325.  
  326. $response = $client->get('//ulogin.ru/token.php', [
  327. 'query' => [
  328. 'token' => $token,
  329. 'host' => $_SERVER['HTTP_HOST'],
  330. ]
  331. ]);
  332.  
  333. if ($response->getStatusCode() === 200) {
  334. $network = json_decode($response->getBody()->getContents());
  335.  
  336. session()->put('social', $network);
  337.  
  338. /** @var Social $social */
  339. $social = Social::query()
  340. ->where('network', $network->network)
  341. ->where('uid', $network->uid)
  342. ->first();
  343.  
  344. if ($social && $user = getUserById($social->user_id)) {
  345. (new self())->rememberUser($user, true);
  346.  
  347. $user->saveVisit(Login::SOCIAL);
  348.  
  349. return $user;
  350. }
  351. }
  352.  
  353. return false;
  354. }
  355.  
  356. /**
  357. * Возвращает название уровня по ключу
  358. *
  359. * @param string $level
  360. * @return string
  361. */
  362. public static function getLevelByKey(string $level): string
  363. {
  364. switch ($level) {
  365. case self::BOSS:
  366. $status = __('main.boss');
  367. break;
  368. case self::ADMIN:
  369. $status = __('main.admin');
  370. break;
  371. case self::MODER:
  372. $status = __('main.moder');
  373. break;
  374. case self::EDITOR:
  375. $status = __('main.editor');
  376. break;
  377. case self::USER:
  378. $status = __('main.user');
  379. break;
  380. case self::PENDED:
  381. $status = __('main.pended');
  382. break;
  383. case self::BANNED:
  384. $status = __('main.banned');
  385. break;
  386. default:
  387. $status = setting('statusdef');
  388. }
  389.  
  390. return $status;
  391. }
  392.  
  393. /**
  394. * Возвращает уровень пользователя
  395. *
  396. * @return string Уровень пользователя
  397. */
  398. public function getLevel(): string
  399. {
  400. return self::getLevelByKey($this->level);
  401. }
  402.  
  403. /**
  404. * Is user online
  405. *
  406. * @return bool
  407. */
  408. public function isOnline(): bool
  409. {
  410. static $visits;
  411.  
  412. if (! $visits) {
  413. $visits = Cache::remember('visit', 10, static function () {
  414. return Online::query()
  415. ->whereNotNull('user_id')
  416. ->pluck('user_id', 'user_id')
  417. ->all();
  418. });
  419. }
  420.  
  421. return isset($visits[$this->id]);
  422. }
  423.  
  424. /**
  425. * User online status
  426. *
  427. * @return HtmlString онлайн-статус
  428. */
  429. public function getOnline(): HtmlString
  430. {
  431. $online = '';
  432.  
  433. if ($this->isOnline()) {
  434. $online = '<div class="user-status bg-success" title="' . __('main.online') . '"></div>';
  435. }
  436.  
  437. return new HtmlString($online);
  438. }
  439.  
  440. /**
  441. * Get last visit
  442. *
  443. * @return string
  444. */
  445. public function getVisit(): string
  446. {
  447. if ($this->isOnline()) {
  448. $visit = __('main.online');
  449. } else {
  450. $visit = dateFixed($this->updated_at);
  451. }
  452.  
  453. return $visit;
  454. }
  455.  
  456. /**
  457. * Возвращает статус пользователя
  458. *
  459. * @return HtmlString|string статус пользователя
  460. */
  461. public function getStatus()
  462. {
  463. static $status;
  464.  
  465. if (! $this->id) {
  466. return setting('statusdef');
  467. }
  468.  
  469. if (! $status) {
  470. $status = $this->getStatuses(6 * 3600);
  471. }
  472.  
  473. if (isset($status[$this->id])) {
  474. return new HtmlString($status[$this->id]);
  475. }
  476.  
  477. return setting('statusdef');
  478. }
  479.  
  480. /**
  481. * Возвращает аватар пользователя
  482. *
  483. * @return HtmlString аватар пользователя
  484. */
  485. public function getAvatar(): HtmlString
  486. {
  487. if (! $this->id) {
  488. return new HtmlString($this->getAvatarGuest());
  489. }
  490.  
  491. if ($this->avatar && file_exists(public_path($this->avatar))) {
  492. $avatar = $this->getAvatarImage();
  493. } else {
  494. $avatar = $this->getAvatarDefault();
  495. }
  496.  
  497. return new HtmlString('<a href="/users/' . $this->login . '">' . $avatar . '</a> ');
  498. }
  499.  
  500. /**
  501. * Возвращает изображение аватара
  502. *
  503. * @return HtmlString
  504. */
  505. public function getAvatarImage(): HtmlString
  506. {
  507. if (! $this->id) {
  508. return $this->getAvatarGuest();
  509. }
  510.  
  511. if ($this->avatar && file_exists(public_path($this->avatar))) {
  512. return new HtmlString('<img class="avatar-default rounded-circle" src="' . $this->avatar . '" alt="">');
  513. }
  514.  
  515. return $this->getAvatarDefault();
  516. }
  517.  
  518. /**
  519. * Get guest avatar
  520. *
  521. * @return HtmlString
  522. */
  523. public function getAvatarGuest(): HtmlString
  524. {
  525. return new HtmlString('<img class="avatar-default rounded-circle" src="/assets/img/images/avatar_guest.png" alt=""> ');
  526. }
  527.  
  528. /**
  529. * Возвращает аватар для пользователя по умолчанию
  530. *
  531. * @return HtmlString код аватара
  532. */
  533. private function getAvatarDefault(): HtmlString
  534. {
  535. $name = $this->getName();
  536. $color = '#' . substr(dechex(crc32($this->login)), 0, 6);
  537. $letter = mb_strtoupper(utfSubstr($name, 0, 1), 'utf-8');
  538.  
  539. return new HtmlString('<span class="avatar-default rounded-circle" style="background:' . $color . '">' . $letter . '</span>');
  540. }
  541.  
  542. /**
  543. * Кеширует статусы пользователей
  544. *
  545. * @param int $seconds время кеширования
  546. *
  547. * @return array
  548. */
  549. public function getStatuses(int $seconds): array
  550. {
  551. return Cache::remember('status', $seconds, static function () {
  552. $users = self::query()
  553. ->select('users.id', 'users.status', 'status.name', 'status.color')
  554. ->leftJoin('status', static function (JoinClause $join) {
  555. $join->whereRaw('users.point between status.topoint and status.point');
  556. })
  557. ->where('users.point', '>', 0)
  558. ->toBase()
  559. ->get();
  560.  
  561. $statuses = [];
  562. foreach ($users as $user) {
  563. if ($user->status) {
  564. $statuses[$user->id] = '<span style="color:#ff0000">' . check($user->status) . '</span>';
  565. continue;
  566. }
  567.  
  568. if ($user->color) {
  569. $statuses[$user->id] = '<span style="color:' . $user->color . '">' . check($user->name) . '</span>';
  570. continue;
  571. }
  572.  
  573. $statuses[$user->id] = check($user->name);
  574. }
  575.  
  576. return $statuses;
  577. });
  578. }
  579.  
  580. /**
  581. * Возвращает находится ли пользователь в контакатх
  582. *
  583. * @param User $user объект пользователя
  584. * @return bool находится ли в контактах
  585. */
  586. public function isContact(User $user): bool
  587. {
  588. $isContact = Contact::query()
  589. ->where('user_id', $this->id)
  590. ->where('contact_id', $user->id)
  591. ->first();
  592.  
  593. if ($isContact) {
  594. return true;
  595. }
  596.  
  597. return false;
  598. }
  599.  
  600. /**
  601. * Возвращает находится ли пользователь в игноре
  602. *
  603. * @param User $user объект пользователя
  604. * @return bool находится ли в игноре
  605. */
  606. public function isIgnore(User $user): bool
  607. {
  608. $isIgnore = Ignore::query()
  609. ->where('user_id', $this->id)
  610. ->where('ignore_id', $user->id)
  611. ->first();
  612.  
  613. if ($isIgnore) {
  614. return true;
  615. }
  616.  
  617. return false;
  618. }
  619.  
  620. /**
  621. * Отправляет приватное сообщение
  622. *
  623. * @param User|null $author Отправитель
  624. * @param string $text Текст сообщения
  625. * @param bool $withAuthor Создавать диалог для автора
  626. *
  627. * @return Builder|Model
  628. */
  629. public function sendMessage(?User $author, string $text, bool $withAuthor = true)
  630. {
  631. return (new Message())->createDialogue($this, $author, $text, $withAuthor);
  632. }
  633.  
  634. /**
  635. * Возвращает количество писем пользователя
  636. *
  637. * @return int количество писем
  638. */
  639. public function getCountMessages(): int
  640. {
  641. return Dialogue::query()->where('user_id', $this->id)->count();
  642. }
  643.  
  644. /**
  645. * Возвращает размер контакт-листа
  646. *
  647. * @return int количество контактов
  648. */
  649. public function getCountContact(): int
  650. {
  651. return Contact::query()->where('user_id', $this->id)->count();
  652. }
  653.  
  654. /**
  655. * Возвращает размер игнор-листа
  656. *
  657. * @return int количество игнорируемых
  658. */
  659. public function getCountIgnore(): int
  660. {
  661. return Ignore::query()->where('user_id', $this->id)->count();
  662. }
  663.  
  664. /**
  665. * Возвращает количество записей на стене сообщений
  666. *
  667. * @return int количество записей
  668. */
  669. public function getCountWall(): int
  670. {
  671. return Wall::query()->where('user_id', $this->id)->count();
  672. }
  673.  
  674. /**
  675. * Удаляет альбом пользователя
  676. *
  677. * @return void
  678. */
  679. public function deleteAlbum(): void
  680. {
  681. $photos = Photo::query()->where('user_id', $this->id)->get();
  682.  
  683. if ($photos->isNotEmpty()) {
  684. foreach ($photos as $photo) {
  685. $photo->comments()->delete();
  686. $photo->delete();
  687. }
  688. }
  689. }
  690.  
  691. /**
  692. * Удаляет записи пользователя из всех таблиц
  693. *
  694. * @return bool|null результат удаления
  695. * @throws Exception
  696. */
  697. public function delete(): ?bool
  698. {
  699. deleteFile(public_path($this->picture));
  700. deleteFile(public_path($this->avatar));
  701.  
  702. Message::query()->where('user_id', $this->id)->delete();
  703. Dialogue::query()->where('user_id', $this->id)->delete();
  704. Contact::query()->where('user_id', $this->id)->delete();
  705. Ignore::query()->where('user_id', $this->id)->delete();
  706. Rating::query()->where('user_id', $this->id)->delete();
  707. Wall::query()->where('user_id', $this->id)->delete();
  708. Note::query()->where('user_id', $this->id)->delete();
  709. Notebook::query()->where('user_id', $this->id)->delete();
  710. Banhist::query()->where('user_id', $this->id)->delete();
  711. Bookmark::query()->where('user_id', $this->id)->delete();
  712. Login::query()->where('user_id', $this->id)->delete();
  713. Invite::query()->where('user_id', $this->id)->orWhere('invite_user_id', $this->id)->delete();
  714.  
  715. return parent::delete();
  716. }
  717.  
  718.  
  719. /**
  720. * Updates count messages
  721. *
  722. * @return void
  723. */
  724. public function updatePrivate(): void
  725. {
  726. if ($this->newprivat) {
  727. $countDialogues = Dialogue::query()
  728. ->where('user_id', $this->id)
  729. ->where('reading', 0)
  730. ->count();
  731.  
  732. if ($countDialogues !== $this->newprivat) {
  733. $this->update([
  734. 'newprivat' => $countDialogues,
  735. 'sendprivatmail' => 0,
  736. ]);
  737. }
  738. }
  739. }
  740.  
  741. /**
  742. * Check user banned
  743. *
  744. * @return bool
  745. */
  746. public function isBanned(): bool
  747. {
  748. return $this->level === self::BANNED;
  749. }
  750.  
  751. /**
  752. * Check user pended
  753. *
  754. * @return bool
  755. */
  756. public function isPended(): bool
  757. {
  758. return setting('regkeys') && $this->level === self::PENDED;
  759. }
  760.  
  761. /**
  762. * Check user active
  763. *
  764. * @return bool
  765. */
  766. public function isActive(): bool
  767. {
  768. return in_array($this->level, self::USER_GROUPS, true);
  769. }
  770.  
  771. /**
  772. * Getting daily bonus
  773. *
  774. * @return void
  775. */
  776. public function gettingBonus(): void
  777. {
  778. if ($this->isActive() && $this->timebonus < strtotime('-23 hours', SITETIME)) {
  779. $this->increment('money', setting('bonusmoney'));
  780. $this->update(['timebonus' => SITETIME]);
  781.  
  782. setFlash('success', __('main.daily_bonus', ['money' => plural(setting('bonusmoney'), setting('moneyname'))]));
  783. }
  784. }
  785.  
  786. /**
  787. * Сохраняет посещения
  788. *
  789. * @param string $type
  790. *
  791. * @return void
  792. */
  793. public function saveVisit(string $type): void
  794. {
  795. $authorization = Login::query()
  796. ->where('user_id', $this->id)
  797. ->where('created_at', '>', SITETIME - 60)
  798. ->first();
  799.  
  800. if (! $authorization) {
  801. Login::query()->create([
  802. 'user_id' => $this->id,
  803. 'ip' => getIp(),
  804. 'brow' => getBrowser(),
  805. 'created_at' => SITETIME,
  806. 'type' => $type,
  807. ]);
  808. }
  809.  
  810. $this->increment('visits');
  811. }
  812.  
  813. /**
  814. * Remember user
  815. *
  816. * @param User $user
  817. * @param bool $remember
  818. */
  819. private function rememberUser(User $user, bool $remember = false): void
  820. {
  821. if ($remember) {
  822. cookie()->queue(cookie()->forever('login', $user->login));
  823. cookie()->queue(cookie()->forever('password', $user->password));
  824. }
  825.  
  826. session()->put('id', $user->id);
  827. session()->put('password', $user->password);
  828. session()->put('online');
  829. }
  830. }