Initial commit

This commit is contained in:
Developer
2025-04-21 16:03:20 +02:00
commit 2832896157
22874 changed files with 3092801 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Domain\Feeds\Action;
use App\Domain\Feeds\ToFeedAction;
use App\Domain\Tags\Action\CreateTagAction;
class CreateFeedAction
{
public $tooFeedAction;
public $tagAction;
public function __construct(ToFeedAction $tooFeedAction, CreateTagAction $tagAction)
{
$this->tooFeedAction = $tooFeedAction;
$this->tagAction = $tagAction;
}
public function __invoke($dataFeed)
{
$feed = ($this->tooFeedAction)($dataFeed);
if(count($dataFeed->tags)){
$idsTag = ($this->tagAction)($dataFeed->tags);
$feed->tags()->sync($idsTag);
}
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace App\Domain\Feeds\DataTransferObjects;
use App\Domain\Feeds\Enums\CollectionMediaEnum;
use App\Domain\Feeds\Service\FeedMediaTransform;
use Spatie\DataTransferObject\DataTransferObject;
class FeedData extends DataTransferObject
{
public static function fromAds($feed)
{
$user = new \stdClass();
$user->id = 999999;
$user->name = 'Реклама';
$user->username = 'Реклама';
$user->color = '#795541';
$user->user_char = 'AD';
return [
'id' => $feed->id,
'type' => $feed->type,
'user' => (array)$user,
'entity' => self::fromDataAds($feed, CollectionMediaEnum::COMMON()),
];
}
public static function fromDataAds($feed, $collection_media_name)
{
$entity = (object) [];
$entity->title = $feed->title;
$entity->body = nl2br($feed->body);
$entity->views_count = $feed->views_count;
$entity->slug = $feed->slug;
$entity->type = $feed->type;
$entity->created_at_humans = $feed->created_at->diffForHumans();
$mediaTransform = (new FeedMediaTransform($feed))
->addCollectionMediaName($collection_media_name)
->spot();
$entity->preview = $mediaTransform['preview'];
$entity->collection_medias = $mediaTransform['medias'];
$entity->is_ads = true;
$entity->is_repost = false;
$entity->price = null;
$entity->is_paid = 0;
$entity->likes = 0;
$entity->liked = false;
$entity->tags = [];
$entity->status = 1;
$entity->comments = 0;
return $entity;
}
public static function fromDataBaseWithUser($feed, $purchased_subscriptions_by_users = [])
{
$feed_is_restrict = false;
$feed_is_restrict_name = '';
$feed_user_id = $feed->user_id;
$is_my_feed = false;
if(auth()->user()){
$is_my_feed = auth()->user()->id === $feed_user_id;
}
$feed_is_semi_paid = $feed->is_paid === 2;
// $is_adult_feed = false;
// $is_my_settings_adult_restrict = auth()->user()->allow_adult_content;
$restrict_feed_adult = false;
// if(($is_my_settings_adult_restrict === false && $is_adult_feed) && $is_my_feed === false){
// $feed_is_restrict_name = 'adults';
// $restrict_feed_adult = true;
// }
$private_user_feed = $feed->user->private;
$restrict_feed_private_account = false;
if(($feed_is_semi_paid && $private_user_feed && !in_array($feed_user_id, $purchased_subscriptions_by_users)) && $is_my_feed === false){
$feed_is_restrict_name = 'prohibited';
$restrict_feed_private_account = true;
}
if($restrict_feed_adult || $restrict_feed_private_account){
$feed_is_restrict = true;
}
$user = $feed->user->only('id', 'name', 'username', 'photo_path', 'color', 'user_char', 'private');
return [
'id' => $feed->id,
'type' => $feed_is_restrict ? $feed_is_restrict_name : $feed->type,
'user' => $user,
'entity' => $feed_is_restrict ? null : self::fromData($feed, CollectionMediaEnum::COMMON()),
];
}
public static function fromData($feed, $collection_media_name)
{
$entity = (object) [];
$entity->title = $feed->title;
$entity->body = $feed->body;
$entity->views_count = $feed->views_count;
$entity->price = $feed->price;
$entity->is_paid = $feed->is_paid;
$entity->is_ads = false;
// $entity->is_adult = $feed->is_adult;
$entity->slug = $feed->slug;
$entity->type = $feed->type;
$entity->is_repost = $feed->is_repost;
$entity->likes = $feed->likes_count;
$entity->liked = $feed->liked ?? false;
$entity->tags = $feed->tags;
$entity->status = $feed->status;
$entity->comments = $feed->comments_count;
$entity->created_at_humans = $feed->created_at->diffForHumans();
$mediaTransform = (new FeedMediaTransform($feed))
->addCollectionMediaName($collection_media_name)
->spot();
$entity->preview = $mediaTransform['preview'];
$entity->collection_medias = $mediaTransform['medias'];
return $entity;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Domain\Feeds\DataTransferObjects;
use Spatie\DataTransferObject\DataTransferObjectCollection;
class FeedDataCollection extends DataTransferObjectCollection
{
public static function create(array $data)
{
return new static(FeedData::arrayOf($data));
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Domain\Feeds\Enums;
use Spatie\Enum\Laravel\Enum;
/**
* @method static self PREVIEW()
* @method static self PAID()
* @method static self COMMON()
*/
final class CollectionMediaEnum extends Enum
{
protected static function values(): array
{
return [
'PREVIEW' => 'preview',
'PAID' => 'paid',
'COMMON' => 'common',
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Domain\Feeds\Enums;
use Spatie\Enum\Laravel\Enum;
/**
* @method static self PENDING()
* @method static self APPROVED()
* @method static self BANNED()
* @method static self EDITABLE()
*/
final class StatusEnum extends Enum
{
protected static function values(): array
{
return [
'PENDING' => 0,
'APPROVED' => 1,
'BANNED' => 2,
'EDITABLE' => 3,
];
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Domain\Feeds\Models;
use App\Models\User;
use App\Models\Model;
use App\Domain\Tags\Models\Tag;
use Spatie\MediaLibrary\HasMedia;
use App\Domain\Feeds\Enums\StatusEnum;
use App\Domain\Comments\Models\Comment;
use App\Domain\Complaints\Models\Complaint;
use Spatie\MediaLibrary\InteractsWithMedia;
use App\Domain\Feeds\Observers\FeedObserver;
use Illuminate\Database\Eloquent\SoftDeletes;
class Feed extends Model implements HasMedia
{
use InteractsWithMedia, SoftDeletes;
public function getUserTypeAttribute()
{
return $this->user->type;
}
protected $casts = [
'is_paid' => 'integer',
'is_ads' => 'boolean',
'status' => StatusEnum::class,
];
protected static function boot()
{
parent::boot();
self::observe(FeedObserver::class);
}
public function feedable()
{
return $this->morphTo();
}
public function tags()
{
return $this->belongsToMany(Tag::class, 'feed_tags');
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function views()
{
return $this->belongsToMany(User::class, 'users_feeds_view');
}
public function likes()
{
return $this->belongsToMany(User::class, 'users_feeds_like');
}
public function user()
{
return $this->belongsTo(User::class);
}
public function who_boughts()
{
return $this->belongsToMany(User::class, 'user_feed_purchase')->withPivot('amount')->withTimestamps();
}
public function complaints()
{
return $this->hasMany(Complaint::class);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Domain\Feeds\Observers;
use Illuminate\Support\Str;
use App\Domain\Feeds\Models\Feed;
use App\Events\FeedAddProcessed;
use App\Events\FeedRemoveProcessed;
use App\Events\FeedUpdateProcessed;
class FeedObserver
{
public function creating(Feed $feed)
{
$uuid = (string) Str::uuid();
$feed->slug = $uuid . '_' . $feed->type;
}
public function created(Feed $feed)
{
FeedAddProcessed::dispatch($feed);
}
public function deleted(Feed $feed)
{
FeedRemoveProcessed::dispatch($feed);
}
// public function forceDeleted(Feed $feed)
// {
// dd('forceDeleted');
// }
public function updated(Feed $feed)
{
FeedUpdateProcessed::dispatch($feed);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Domain\Feeds\Observers;
use App\Models\User;
use App\Domain\Feeds\Models\Feed;
class NovaFeedAdsObserver
{
public function creating(Feed $feed){
if(str_contains( request()->getPathInfo(), '/nova-api/feed-ads')){
$user = User::system();
$feed->user_id = $user->id;
$feed->is_ads = true;
$feed->created_at = now();
$feed->updated_at = now();
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Domain\Feeds\Observers;
use App\Domain\Feeds\Models\Feed;
use App\Notifications\RemoveFeed;
use App\Domain\Feeds\Enums\StatusEnum;
use App\Notifications\BannedMessageFeed;
class NovaFeedObserver
{
public function updating(Feed $feed)
{
$oldStatus = $feed->getOriginal('status');
$newStatus = $feed->status;
if(($oldStatus === StatusEnum::PENDING() || $oldStatus === StatusEnum::EDITABLE()) && $newStatus === StatusEnum::BANNED()){
$user = $feed->user;
$message = [
'user_id' => $user->id,
'node_id' => $feed->id,
'text' => $feed->status_note,
'success' => false
];
$user->notify(new BannedMessageFeed($message));
}
if(($oldStatus === StatusEnum::PENDING() || $oldStatus === StatusEnum::EDITABLE()) && $newStatus === StatusEnum::APPROVED()){
$user = $feed->user;
$message = [
'user_id' => $user->id,
'node_id' => $feed->id,
'text' => $feed->status_note,
'success' => true
];
$user->notify(new BannedMessageFeed($message));
$feed->status_note = '';
}
}
public function deleting(Feed $feed)
{
if(!$feed->trashed()){
$complaints = $feed->complaints;
foreach ($complaints as $complaint) {
$complaint->status = 'reviewed_bad';
$complaint->moderator_checking_id = auth()->id();
$complaint->save();
}
$user = $feed->user;
$message = [
'user_id' => $user->id,
'node_id' => $feed->id,
];
$user->notify(new RemoveFeed($message));
}
}
}

View File

@@ -0,0 +1,262 @@
<?php
namespace App\Domain\Feeds\Queries;
use App\Domain\Feeds\Models\Feed;
use App\Domain\Feeds\Enums\StatusEnum;
use Illuminate\Database\Eloquent\Builder;
use App\Domain\Feeds\DataTransferObjects\FeedData;
use App\Domain\Subscriptions\Service\SubscriptionService;
class FeedQueryBuilder
{
const PAGINATION_COUNT = 10;
public $feed;
public $pagination = true;
public $onlyActiveFeed = true;
public $nextCursor;
public $is_ads = false;
public $hasPages;
public function __construct($query = null)
{
if($query){
$this->feed = $query;
}else{
$this->feed = Feed::query();
}
}
public function enableAdvertising()
{
$this->is_ads = true;
return $this;
}
public function search($filters)
{
$this->feed->when($filters['search'] ?? null, function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->where('title', 'ilike', '%'.$search.'%')
->orWhere('body', 'ilike', '%'.$search.'%');
});
});
return $this;
}
public function addTags($tags)
{
$this->feed->whereHas('tags', function (Builder $query) use($tags) {
$query->whereIn('id', $tags);
});
return $this;
}
public function addType($type)
{
if($type == 'image'){
//$this->feed->whereHasMorph('feedable', [Image::class]);
$this->feed->where('type', 'images');
}
if($type == 'video'){
// $this->feed->whereHasMorph('feedable', [Video::class]);
$this->feed->where('type', 'videos');
}
if($type == 'music'){
//$this->feed->whereHasMorph('feedable', [Music::class]);
$this->feed->where('type', 'musics');
}
return $this;
}
public function filter($filter = null)
{
if($filter === 'hot'){
return $this->order('likes_count');
}
return $this->order();
}
public function order($by = null)
{
if($by){
$this->feed->orderBy($by, 'desc');
}else{
$this->feed->orderBy('id', 'desc');
}
return $this;
}
public function selectBy($where, $by)
{
$this->feed->where($where, $by);
return $this;
}
public function selectByIds($ids)
{
if(is_array($ids)){
$this->feed->whereIn('id', $ids);
}else{
$this->feed->where('id', $ids);
}
return $this;
}
public function withTrashed() {
$this->feed->withTrashed();
return $this;
}
public function disableActiveFeed()
{
$this->onlyActiveFeed = false;
return $this;
}
public function disablePaginate()
{
$this->pagination = false;
return $this;
}
public function getData()
{
if($this->onlyActiveFeed){
$this->feed = $this->feed->where('status', StatusEnum::APPROVED());
}
$this->feed = $this->feed->withCount([
'comments',
'likes',
]);
if(auth()->user()){
$this->feed = $this->feed
->withCount(['likes as liked' => function (Builder $query) {
$query->where('user_id', auth()->user()->id);
}])
->withCasts(['liked' => 'boolean']);
}
if($this->pagination){
$this->feed = $this->feed->cursorPaginate(self::PAGINATION_COUNT);
$this->getCursorHash();
}else{
$this->feed = $this->feed->get();
}
return $this;
}
protected function getCursorHash()
{
$this->hasPages = $this->feed->hasMorePages();
$this->nextCursor = get_cursor_hash($this->feed);
}
public function withTags()
{
$this->feed->with('tags:id,name,slug');
return $this;
}
public function withUser()
{
$this->feed->with('user:id,first_name,last_name,username,photo_path,color,user_char,private');
return $this;
}
public function withFeedable()
{
// $this->feed->with('media');
// :id,model_type,model_id,name,file_name,collection_name,custom_properties
if(auth()->user()){
$this->feed->withCount('media')
->with(['media' => function($query){
// withCount('likes')->withCasts(['likes_count' => 'boolean']);
$query->withCount(['likes' => function (Builder $query) {
$query->where('user_id', auth()->user()->id);
}])->withCasts(['likes_count' => 'boolean']);
}]);
}else{
$this->feed->with('media');
}
return $this;
// $this->feed->with('feedable', function (MorphTo $morphTo) {
// $morphTo->morphWith([
// Image::class => ['media'],
// Video::class => ['media'],
// Music::class => ['media'],
// ]);
// });
// return $this;
}
public function transformData()
{
$feed_collect = collect();
if(auth()->user()){
$purchased_subscriptions_by_users = \DB::table('users_package_customers')
->where('customer_id', auth()->user()->id)
->where('time_end', '>', now())
->pluck('user_id')->toArray();
}else{
$purchased_subscriptions_by_users = [];
}
foreach ($this->feed as $feed) {
$feed = FeedData::fromDataBaseWithUser($feed, $purchased_subscriptions_by_users);
if($feed['entity']){
$feed['entity']->tags->transform(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'slug' => $item->slug,
];
});
}
$feed_collect[] = $feed;
}
if(auth()->check()){
$subscription_purchased = SubscriptionService::activeSubscription();
if($subscription_purchased == false){
$adsMedia = Feed::where('is_ads', true)->with('media')->inRandomOrder()->first();
if($adsMedia && $this->is_ads){
$adsMediaData = FeedData::fromAds($adsMedia);
$feed_collect->splice(4, 0, [$adsMediaData]);
}
}
}
return $feed_collect;
}
public function build()
{
$this
->withFeedable()
->withUser()
->withTags()
->getData();
return $this;
}
public function transformGet()
{
return $this
->withFeedable()
->withUser()
->withTags()
->getData()->transformData();
}
}

View File

@@ -0,0 +1,139 @@
<?php
namespace App\Domain\Feeds\Service;
use App\Domain\Feeds\Models\Feed;
use App\Domain\Feeds\Enums\CollectionMediaEnum;
class FeedMediaTransform
{
public $feed;
public $type;
public $typeUrl = 'web';
public $collection_name;
public function __construct(Feed $feed)
{
$this->feed = $feed;
$this->type = $feed->type;
}
public function getFullPath()
{
$this->typeUrl = 'disk';
return $this;
}
public function paid()
{
return $this->addCollectionMediaName(CollectionMediaEnum::PAID());
}
public function default()
{
return $this->addCollectionMediaName(CollectionMediaEnum::COMMON());
}
public function addCollectionMediaName($collection_name)
{
$this->collection_name = $collection_name;
return $this;
}
public function spot()
{
if($this->type === 'images'){
return $this->images();
}
if($this->type === 'videos'){
return $this->videos();
}
if($this->type === 'musics'){
return $this->musics();
}
}
public function videos()
{
$collection = [];
$medias = $this->feed->getMedia($this->collection_name);
$medias->each(function ($item) use (&$collection) {
$collection[] = [
'url' => $this->typeUrl === 'web' ? $item->getFullUrl() : $item->getPath(),
'id' => $item->id,
];
});
return [
'preview' => $this->getPreview(),
'medias' => $collection,
];
}
public function images()
{
$collection = [];
$medias = $this->feed->getMedia($this->collection_name);
$medias->each(function ($item) use (&$collection) {
$collection[] = [
'url' => $this->typeUrl === 'web' ? $item->getFullUrl() : $item->getPath(),
'id' => $item->id,
];
});
return [
'preview' => $this->getPreview(),
'medias' => $collection,
];
}
public function musics()
{
$collection = [];
$medias = $this->feed->getMedia($this->collection_name);
$medias->each(function ($item) use (&$collection) {
$collection[] = [
'url' => $this->typeUrl === 'web' ? $item->getFullUrl() : $item->getPath(),
'name' => $item->name,
'playing' => false,
'liked' => $item->likes_count ?? false,
'time' => $item->getCustomProperty('time'),
'id' => $item->id,
];
});
return [
'preview' => $this->getPreview(),
'medias' => $collection,
];
}
public function getPreview($type = 0)
{
if($this->type === 'images'){
$img_preview = $this->feed->getMedia($this->collection_name)->first();
if($img_preview){
if($type){
return $img_preview;
}
return $img_preview->getFullUrl();
}
}
$preview = $this->feed->getMedia(CollectionMediaEnum::PREVIEW());
if($type){
return $preview->count() ? $preview->first() : null;
}
return $preview->count() ? $preview->first()->getFullUrl() : null;
}
public function getPreviewObject()
{
$preview = $this->getPreview(1);
if($preview){
return [ 'id'=> $preview->id, 'url' => $preview->getFullUrl() ];
}
return null;
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Domain\Feeds\Service;
use App\Domain\Feeds\DataTransferObjects\FeedData;
use App\Domain\Feeds\Models\Feed;
use App\Domain\Images\Models\Image;
use App\Domain\Musics\Models\Music;
use App\Domain\Videos\Models\Video;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Builder;
class FeedService
{
public static function list($users, $where = null, $only = null)
{
$users_id = $users->pluck('id')->toArray();
$feeds = Feed::query();
if ($where) {
$feeds = $feeds->where('id', '<', $where);
}
if($only){
if($only == 'image'){
//$feeds = $feeds->whereHasMorph('feedable', [Image::class]);
$feeds = $feeds->where('type', 'images');
}
if($only == 'video'){
//$feeds = $feeds->whereHasMorph('feedable', [Video::class]);
$feeds = $feeds->where('type', 'videos');
}
if($only == 'music'){
//$feeds = $feeds->whereHasMorph('feedable', [Music::class]);
$feeds = $feeds->where('type', 'musics');
}
}
$feeds = $feeds->whereIn('user_id', $users_id)
->orderBy('id', 'desc')
->limit(10)
->withCount([ 'comments', 'likes', 'likes as liked' => function (Builder $query) {
$query->where('user_id', auth()->user()->id);
}])
->withCasts(['liked' => 'boolean'])
->with('tags', 'media')->get();
$feed_collect = collect();
foreach ($feeds as $feed) {
$feed = FeedData::fromDataBase($feed, $users);
$feed['entity']->tags->transform(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'slug' => $item->slug,
];
});
$feed_collect[] = $feed;
}
return $feed_collect;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Domain\Feeds\Service;
use DB;
class LiveFeed
{
public static function addBySub($user)
{
$userID = $user->id;
$userFeeds = $user->feeds()->pluck('created_at', 'id')->transform(function ($item) {
return ['times' => $item->getTimestamp()];
})->toArray();
$add_posts = [];
foreach ($userFeeds as $feedID => $userFeed) {
$add_posts[] = [
'feed_id' => $feedID,
'user_id' => auth()->user()->id,
'home_user_id' => $userID,
'times' => $userFeed['times'],
];
}
DB::table('users_live_feeds')->insertOrIgnore($add_posts);
}
public static function removeBySub($user)
{
$userID = $user->id;
DB::table('users_live_feeds')
->where('home_user_id', $userID)
->where('user_id', auth()->user()->id)
->delete();
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Domain\Feeds;
interface ToFeedAction
{
}