feat: movie download monitor
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
"ext-iconv": "*",
|
||||
"1tomany/rich-bundle": "^1.8",
|
||||
"aimeos/map": "^3.12",
|
||||
"carbondate/carbon": "*",
|
||||
"doctrine/dbal": "^3",
|
||||
"doctrine/doctrine-bundle": "^2.14",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||
|
||||
53
composer.lock
generated
53
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "94d3dbf218bac7512ce9ced86ff066aa",
|
||||
"content-hash": "909a198b48a473bd53de5b78a2abac1c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "1tomany/data-uri",
|
||||
@@ -175,6 +175,57 @@
|
||||
},
|
||||
"time": "2025-03-05T09:16:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "carbondate/carbon",
|
||||
"version": "1.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CarbonDate/Carbon.git",
|
||||
"reference": "a1dd1ad9abfc8b3c4d8768068e6c71d293424e86"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/CarbonDate/Carbon/zipball/a1dd1ad9abfc8b3c4d8768068e6c71d293424e86",
|
||||
"reference": "a1dd1ad9abfc8b3c4d8768068e6c71d293424e86",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Carbon": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Brian Nesbitt",
|
||||
"email": "brian@nesbot.com",
|
||||
"homepage": "http://nesbot.com"
|
||||
}
|
||||
],
|
||||
"description": "A simple API extension for DateTime.",
|
||||
"homepage": "http://carbon.nesbot.com",
|
||||
"keywords": [
|
||||
"date",
|
||||
"datetime",
|
||||
"time"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/CarbonDate/Carbon/issues",
|
||||
"source": "https://github.com/CarbonDate/Carbon/tree/1.17.0"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2015-03-08T14:05:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "3.4.3",
|
||||
|
||||
41
migrations/Version20250503034641.php
Normal file
41
migrations/Version20250503034641.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250503034641 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE movie_monitor (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, title VARCHAR(255) DEFAULT NULL, imdb_id VARCHAR(255) NOT NULL, tmdb_id VARCHAR(255) NOT NULL, status VARCHAR(255) NOT NULL, search_count INT DEFAULT NULL, last_search DATETIME DEFAULT NULL, created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', downloaded_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)', INDEX IDX_C183DBABA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE movie_monitor ADD CONSTRAINT FK_C183DBABA76ED395 FOREIGN KEY (user_id) REFERENCES user (id)
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE movie_monitor DROP FOREIGN KEY FK_C183DBABA76ED395
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE movie_monitor
|
||||
SQL);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Download\Action\Command\MonitorMovieCommand;
|
||||
use App\Download\Action\Handler\MonitorMovieHandler;
|
||||
use App\Download\Framework\Entity\MovieMonitor;
|
||||
use App\Download\Framework\Repository\DownloadRepository;
|
||||
use App\Download\Framework\Repository\MovieMonitorRepository;
|
||||
use App\Tmdb\Tmdb;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
13
src/Download/Action/Command/MonitorMovieCommand.php
Normal file
13
src/Download/Action/Command/MonitorMovieCommand.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Action\Command;
|
||||
|
||||
use App\Download\Framework\Entity\MovieMonitor;
|
||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||
|
||||
class MonitorMovieCommand implements CommandInterface
|
||||
{
|
||||
public function __construct(
|
||||
public int $movieMonitorId,
|
||||
) {}
|
||||
}
|
||||
60
src/Download/Action/Handler/MonitorMovieHandler.php
Normal file
60
src/Download/Action/Handler/MonitorMovieHandler.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Action\Handler;
|
||||
|
||||
use App\Download\Action\Command\DownloadMediaCommand;
|
||||
use App\Download\Action\Command\MonitorMovieCommand;
|
||||
use App\Download\Action\Result\MonitorMovieResult;
|
||||
use App\Download\Framework\Repository\MovieMonitorRepository;
|
||||
use App\Download\Service\MonitorOptionEvaluator;
|
||||
use App\Torrentio\Action\Command\GetMovieOptionsCommand;
|
||||
use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
|
||||
use DateTimeImmutable;
|
||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||
use OneToMany\RichBundle\Contract\HandlerInterface;
|
||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
/** @implements HandlerInterface<MonitorMovieCommand> */
|
||||
readonly class MonitorMovieHandler implements HandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private MovieMonitorRepository $movieMonitorRepository,
|
||||
private GetMovieOptionsHandler $getMovieOptionsHandler,
|
||||
private MonitorOptionEvaluator $monitorOptionEvaluator,
|
||||
private MessageBusInterface $bus,
|
||||
) {}
|
||||
|
||||
public function handle(CommandInterface $command): ResultInterface
|
||||
{
|
||||
$monitor = $this->movieMonitorRepository->find($command->movieMonitorId);
|
||||
$results = $this->getMovieOptionsHandler->handle(
|
||||
new GetMovieOptionsCommand($monitor->getTmdbId(), $monitor->getImdbId())
|
||||
);
|
||||
|
||||
$result = $this->monitorOptionEvaluator->evaluateOptions($monitor, $results->results);
|
||||
|
||||
if (null !== $result) {
|
||||
$this->bus->dispatch(new DownloadMediaCommand(
|
||||
$result->url,
|
||||
$result->title,
|
||||
$result->filename,
|
||||
'movies',
|
||||
$monitor->getImdbId(),
|
||||
));
|
||||
$monitor->setStatus('Complete');
|
||||
$monitor->setDownloadedAt(new DateTimeIMmutable());
|
||||
}
|
||||
|
||||
$monitor->setLastSearch(new DateTimeImmutable());
|
||||
$monitor->setSearchCount($monitor->getSearchCount() + 1);
|
||||
$this->movieMonitorRepository->getEntityManager()->flush();
|
||||
|
||||
return new MonitorMovieResult(
|
||||
status: 'OK',
|
||||
result: [
|
||||
'monitor' => $monitor,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
24
src/Download/Action/Input/MonitorMovieInput.php
Normal file
24
src/Download/Action/Input/MonitorMovieInput.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Action\Input;
|
||||
|
||||
use App\Download\Action\Command\MonitorMovieCommand;
|
||||
use OneToMany\RichBundle\Attribute\SourceRoute;
|
||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||
use OneToMany\RichBundle\Contract\InputInterface;
|
||||
|
||||
class MonitorMovieInput implements InputInterface
|
||||
{
|
||||
public function __construct(
|
||||
#[SourceRoute('tmdbId')]
|
||||
public string $tmdbId,
|
||||
|
||||
#[SourceRoute('imdbId')]
|
||||
public string $imdbId,
|
||||
) {}
|
||||
|
||||
public function toCommand(): CommandInterface
|
||||
{
|
||||
return new MonitorMovieCommand($this->tmdbId, $this->imdbId);
|
||||
}
|
||||
}
|
||||
13
src/Download/Action/Result/MonitorMovieResult.php
Normal file
13
src/Download/Action/Result/MonitorMovieResult.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Action\Result;
|
||||
|
||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||
|
||||
class MonitorMovieResult implements ResultInterface
|
||||
{
|
||||
public function __construct(
|
||||
public string $status,
|
||||
public array $result,
|
||||
) {}
|
||||
}
|
||||
158
src/Download/Framework/Entity/MovieMonitor.php
Normal file
158
src/Download/Framework/Entity/MovieMonitor.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Framework\Entity;
|
||||
|
||||
use App\Download\Framework\Repository\MovieMonitorRepository;
|
||||
use App\User\Framework\Entity\User;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: MovieMonitorRepository::class)]
|
||||
class MovieMonitor
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'yes')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $imdbId = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $tmdbId = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $status = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $searchCount = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?\DateTimeInterface $lastSearch = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?\DateTimeImmutable $createdAt = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?\DateTimeImmutable $downloadedAt = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(?string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImdbId(): ?string
|
||||
{
|
||||
return $this->imdbId;
|
||||
}
|
||||
|
||||
public function setImdbId(string $imdbId): static
|
||||
{
|
||||
$this->imdbId = $imdbId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTmdbId(): ?string
|
||||
{
|
||||
return $this->tmdbId;
|
||||
}
|
||||
|
||||
public function setTmdbId(string $tmdbId): static
|
||||
{
|
||||
$this->tmdbId = $tmdbId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): ?string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setStatus(string $status): static
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSearchCount(): ?int
|
||||
{
|
||||
return $this->searchCount;
|
||||
}
|
||||
|
||||
public function setSearchCount(?int $searchCount): static
|
||||
{
|
||||
$this->searchCount = $searchCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLastSearch(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->lastSearch;
|
||||
}
|
||||
|
||||
public function setLastSearch(?\DateTimeInterface $lastSearch): static
|
||||
{
|
||||
$this->lastSearch = $lastSearch;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDownloadedAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->downloadedAt;
|
||||
}
|
||||
|
||||
public function setDownloadedAt(?\DateTimeImmutable $downloadedAt): static
|
||||
{
|
||||
$this->downloadedAt = $downloadedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
43
src/Download/Framework/Repository/MovieMonitorRepository.php
Normal file
43
src/Download/Framework/Repository/MovieMonitorRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Framework\Repository;
|
||||
|
||||
use App\Download\Framework\Entity\MovieMonitor;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<MovieMonitor>
|
||||
*/
|
||||
class MovieMonitorRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, MovieMonitor::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return MovieMonitor[] Returns an array of MovieMonitor objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('m')
|
||||
// ->andWhere('m.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('m.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?MovieMonitor
|
||||
// {
|
||||
// return $this->createQueryBuilder('m')
|
||||
// ->andWhere('m.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
91
src/Download/Service/MonitorOptionEvaluator.php
Normal file
91
src/Download/Service/MonitorOptionEvaluator.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Download\Service;
|
||||
|
||||
use Aimeos\Map;
|
||||
use App\Download\Framework\Entity\MovieMonitor;
|
||||
use App\Torrentio\Result\TorrentioResult;
|
||||
|
||||
class MonitorOptionEvaluator
|
||||
{
|
||||
/**
|
||||
* @param MovieMonitor $monitor
|
||||
* @param TorrentioResult[] $results
|
||||
* @return TorrentioResult|null
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function evaluateOptions(MovieMonitor $monitor, array $results): ?TorrentioResult
|
||||
{
|
||||
$sizeLow = 500;
|
||||
$sizeHigh = 4096;
|
||||
|
||||
$bestMatches = [];
|
||||
$matches = [];
|
||||
|
||||
$userPreferences = $monitor->getUser()->getUserPreferenceValues();
|
||||
|
||||
foreach ($results as $result) {
|
||||
if (!in_array($userPreferences['language'], $result->languages)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($result->resolution === $userPreferences['resolution']
|
||||
&& $result->codec === $userPreferences['codec']
|
||||
) {
|
||||
$bestMatches[] = $result;
|
||||
}
|
||||
|
||||
if ($userPreferences['resolution'] === '2160p'
|
||||
&& $userPreferences['codec'] === $result->codec
|
||||
&& $result->resolution === '1080p'
|
||||
) {
|
||||
$matches[] = $result;
|
||||
}
|
||||
|
||||
if ($userPreferences['codec'] === 'h264'
|
||||
&& $userPreferences['resolution'] === $result->resolution
|
||||
&& $result->codec === 'h265'
|
||||
) {
|
||||
$matches[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
$sizeMatches = [];
|
||||
|
||||
foreach ($bestMatches as $result) {
|
||||
$size = 0;
|
||||
if (str_contains($result->size, 'GB')) {
|
||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
||||
} else {
|
||||
$size = (int) trim(str_replace('MB', '', $result->size));
|
||||
}
|
||||
|
||||
if ($size > $sizeLow && $size < $sizeHigh) {
|
||||
$sizeMatches[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($sizeMatches)) {
|
||||
return Map::from($sizeMatches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
||||
}
|
||||
|
||||
foreach ($matches as $result) {
|
||||
$size = 0;
|
||||
if (str_contains($result->size, 'GB')) {
|
||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
||||
} else {
|
||||
$size = (int) trim(str_replace('MB', '', $result->size));
|
||||
}
|
||||
|
||||
if ($size > $sizeLow && $size < $sizeHigh) {
|
||||
$sizeMatches[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($sizeMatches)) {
|
||||
return Map::from($sizeMatches)->usort(fn($a, $b) => $a->seeders <=> $b->seeders)->last();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,14 @@ use Nihilarr\PTN;
|
||||
|
||||
class ResultFactory
|
||||
{
|
||||
public static $codecMap = [
|
||||
'h264' => 'h264',
|
||||
'h265' => 'h265',
|
||||
'x264' => 'h264',
|
||||
'x265' => 'h265',
|
||||
'-' => '-'
|
||||
];
|
||||
|
||||
public static function map(
|
||||
string $url,
|
||||
string $title,
|
||||
@@ -25,7 +33,7 @@ class ResultFactory
|
||||
$ptn->season ?? "-",
|
||||
$bingeGroup,
|
||||
$ptn->resolution ?? "-",
|
||||
$ptn->codec ?? "-",
|
||||
self::setCodec($ptn->codec ?? "-"),
|
||||
$ptn,
|
||||
substr(base64_encode($url), strlen($url) - 10),
|
||||
$ptn->episode ?? "-",
|
||||
@@ -101,6 +109,11 @@ class ResultFactory
|
||||
}
|
||||
}
|
||||
|
||||
public static function setCodec(string $codec): string
|
||||
{
|
||||
return self::$codecMap[strtolower($codec)] ?? $codec;
|
||||
}
|
||||
|
||||
private static function setEpisode(string $title)
|
||||
{
|
||||
$value = [];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace App\User\Framework\Entity;
|
||||
|
||||
use Aimeos\Map;
|
||||
use App\User\Framework\Repository\PreferencesRepository;
|
||||
use App\Download\Framework\Entity\MovieMonitor;
|
||||
use App\User\Framework\Repository\UserRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
@@ -40,9 +40,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
#[ORM\OneToMany(targetEntity: UserPreference::class, mappedBy: 'user', cascade: ['persist', 'remove'])]
|
||||
private Collection $userPreferences;
|
||||
|
||||
/**
|
||||
* @var Collection<int, MovieMonitor>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: MovieMonitor::class, mappedBy: 'user', orphanRemoval: true)]
|
||||
private Collection $yes;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->userPreferences = new ArrayCollection();
|
||||
$this->yes = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
@@ -204,4 +211,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, MovieMonitor>
|
||||
*/
|
||||
public function getYes(): Collection
|
||||
{
|
||||
return $this->yes;
|
||||
}
|
||||
|
||||
public function addYe(MovieMonitor $ye): static
|
||||
{
|
||||
if (!$this->yes->contains($ye)) {
|
||||
$this->yes->add($ye);
|
||||
$ye->setUser($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeYe(MovieMonitor $ye): static
|
||||
{
|
||||
if ($this->yes->removeElement($ye)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($ye->getUser() === $this) {
|
||||
$ye->setUser(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user