D
D
Di Lee2020-04-23 11:24:47
symfony
Di Lee, 2020-04-23 11:24:47

How to competently improve your service on Symfony in terms of the OOP approach?

Hello. I am starting to learn Symfony and OOP. Wrote my small test project. But it is felt that there is a g###code.
Help / recommend how to change it, what to fix, so that it is competent from the standpoint of the OOP approach.

The essence of the service: Parsing - collecting news from a news site and immersing them in the database, as well as displaying a list of news. The main question is the literacy of the service structure in terms of the OOP approach

Controller class PostController :

use App\Entity\Post;
use Doctrine\ORM\EntityManagerInterface;
use PHPHtmlParser\Dom;
use GuzzleHttp\Client;

class PostController extends AbstractController
{
    private $postRepository;
    public function __construct(PostRepository $postRepository)
    {
        $this->postRepository = $postRepository;
    }
    /**
     * @Route("/", name="posts")
     */
    public function index(Request $request, RbcParseClient $parse): Response
    {
        if ($request->query->get('update') && $request->query->get('update') == '1'){
            $parse->updateNews();
        }
        $posts = $this->postRepository->findLastNews();
        return $this->render('post/posts.html.twig', [
            'posts' => $posts
        ]);
    }

}


The main class of the RbcParseClient service to which a request is made for collecting and parsing news (now the request is from the controller, but this is not important, you can also do it from the cron file):

use App\Entity\Post;
use Doctrine\ORM\EntityManagerInterface;
use PHPHtmlParser\Dom;
use GuzzleHttp\Client;

class RbcParseClient
{
    private $dom;
    private $em;
    private $checkPosts;

    public function __construct(EntityManagerInterface $em)
    {
        $this->dom = new Dom();
        $this->em = $em;
        $postRepository = $this->em->getRepository(Post::class);
        $this->checkPosts = $postRepository->findLastNews();
    }

    public function updateNews(): void
    {
        $res = self::getUrlContent('https://www.rbc.ru');

        $this->dom->load($res);
        $contents = $this->dom->find('.news-feed__wrapper .js-news-feed-list')->find('a.news-feed__item');

        foreach ($contents as $content)
        {
            $link = $content->getAttribute('href');

            $resDomContent = self::getUrlContent($link);
            $this->dom->load($resDomContent);

            if (preg_match("/style.rbc/", $link) ) {
                $currentNewsItem = new StyleItem($this->dom, $link);
            }elseif(preg_match("/agrodigital.rbc/", $link)){
                $currentNewsItem = new AgroItem($this->dom, $link);
            }else {
                $currentNewsItem = new RegularItem($this->dom, $link);
            }

            $currentNewsItem->parseNewsItem();

            $date = self::getItemDate($content);
            $currentNewsItem->setDate($date);

            $post = new Post();

            $position = $date->format('U');

            $post->setTitle($currentNewsItem->getTitle());
            $post->setBody($currentNewsItem->getBody());
            $post->setImage($currentNewsItem->getImage());
            $post->setLink($currentNewsItem->getLink());
            $post->setCreatedAt($date);
            $post->setPosition($position);

            $this->em->persist($post);

        }
        $this->em->flush();
    }

    public static function getItemDate(Dom\HtmlNode $content): \DateTime
    {
        $dateInfo = $content
            ->find('.news-feed__item__date .news-feed__item__date-text')->innerHTML;
        $time = explode(" ", $dateInfo)[1];
        $date = new \DateTime("now");
        $timeSet = explode(":",$time);
        $date->setTime((int)$timeSet[0], (int)$timeSet[1]);
        return $date;
    }

    public static function getUrlContent(string $url): string
    {
        $client = new Client([ 'base_uri' => $url ]);
        $response = $client->request('GET');
        $res = $response->getBody();
        return $res;
    }
}


Auxiliary service classes RegularItem, StyleItem, AgroItem, etc. Here is an example of one RegularItem class. The rest have slight differences depending on the parsing features:

use PHPHtmlParser\Dom;

class RegularItem extends AbstractParseManager
{
    public function parseNewsItem(): void
    {
        if ($this->dom->find(".js-rbcslider")[0]) {
            $this->parseTitle();
            $this->parseImg();
            $this->parseContent();
        }else{
            $this->title = $this->image = $this->body = "";
        }
    }

    private function parseTitle(): void
    {
        try {
            $artTitle = $this->dom->find(".js-rbcslider")[0]->find('.article__header .article__header__title .js-slide-title')->innerHtml;
            $this->title = $artTitle;
        }catch(\Exception $e){
            $this->title = "";
        }
    }

    private function parseImg(){
        try {
            $artImg = $this->dom->find(".js-rbcslider")[0]->find('.article__text .article__main-image img');
            $this->image = $artImg->getAttribute('src');
        }catch(\Exception $e){
            $this->image =  "";
        }
    }

    private function parseContent(){
        try {
            $artContents = $this->dom->find(".js-rbcslider")[0]->find('.article .article__text');
            $html = "";
            $domContent = new Dom();
            foreach ($artContents as $content)
            {
                $domContent->load($content);
                $artStrings = $domContent->find("p");
                foreach ($artStrings as $string) {
                    $str = strip_tags($string->innerHtml);
                    $html .= "<br />" . trim($str);
                }
            }
            $this->body = $html;
        }catch(\Exception $e){
            $this->body = "";
        }
    }
}


Abstract class manager AbstractParseManager:

use PHPHtmlParser\Dom;
abstract class AbstractParseManager
{
    protected $title;
    protected $image;
    protected $body;
    protected $link;
    protected $date;
    protected $dom;

    public function __construct(Dom $dom, string $link)
    {
        $this->link = $link;
        $this->dom = $dom;
    }

    abstract public function parseNewsItem(): void ;
    public function getTitle()
    {
        return $this->title;
    }
    public function getBody()
    {
        return $this->body;
    }
    public function getImage()
    {
        return $this->image;
    }
    public function getLink()
    {
        return $this->link;
    }
    public function getDate()
    {
        return $this->date;
    }
    public function setDate(\DateTime $date): void
    {
        $this->date = $date;
    }
}


Repository class:
use App\Entity\Post;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;

class PostRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Post::class);
    }
    public function findLastNews()
    {
        return $this->createQueryBuilder('p')
            ->orderBy('p.position', 'DESC')
            ->setMaxResults(15)
            ->getQuery()
            ->getResult()
            ;
    }
}


I don’t publish the Post entity class - there is an ordinary doctrine entity without any features

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question