Friday, August 28, 2015

Build a PHP website using Symfony 2 components and Bootstrap

After a short trip into Silex and Pimple I moved a step forward into understanding frameworks and design patterns. I decided to  build on shoulders of giants by using components from Symfony 2 framework.

My starting point was this tutorial: "http://www.sitepoint.com/build-php-framework-symfony-components/" You should read it first before reading the rest of this post. I followed this  tutorial with small exceptions:
   - added "public" folder where I store index.php and .htaccess
   - added "CPANA\Framework" folder to store Core.php and RequestEvent.php
   - added to the file "vendor/composer/autoload_psr4.php" the path to my files:  'CPANA\\Framework\\' =>array( $baseDir.'/CPANA/Framework'),


Organize the code further
- create structure for Controllers, Models, Views under CPANA directory
- add to vendor/composer/autoload_psr4.php path to my new folders:
    'CPANA\\Controllers\\' =>array( $baseDir.'/CPANA/Controllers'),
    'CPANA\\Models\\' =>array( $baseDir.'/CPANA/Models'),
    'CPANA\\Views\\' =>array( $baseDir.'/CPANA/Views')
,
NOTE!!! If you use composer to install other libraries it will overwrite the file autoload_psr4.php you need to paste again these values
- create basic controller class Pages under "Controllers" folder
- modified the index.php to map Pages::Home method with path '/'

        $app->map('/','CPANA\Controllers\Pages::Home');

!!!!!In order for call_usern_func_array() to be able to call the object method, it needs the fully qualified namesapce: http://stackoverflow.com/questions/14682356/relative-namespaces-and-call-user-func
---------------------------------------------------------------------------------------------------------------
--------------------  handling routes with parameters  ---------------------------------------------------
----------------------------------------------------------------------------------------------------------------
$app->map('/hello/{name}','CPANA\Controllers\Pages::Hello');

in Core.php

$matcher = new UrlMatcher($this->routes, $context);
       
        try {
            $attributes = $matcher->match($request->getPathInfo());


The match method tries to match the URL against a known route pattern, and returns the corresponding route attributes in case of success           
If we check what is stored in $attributes with var_dump($attributes) we can see:

array (size=3)
  'controller' => string 'CPANA\Controllers\Pages::Hello' (length=30)
  'name' => string 'Mela' (length=4)
  '_route' => string '/hello/{name}' (length=13)


 The parameters are taken in order by call_user_func_array function. The first one is "controller" but we do not need that, this is  why we will unset it. Now the first parameter in list is 'name' and our method:  Hello() accepts just one parameter, so it will work with the value stored in $attributes['name']. If we need also the second parameter (in this case '__route') we will add another
 parameter in our method declaration Hello($name, $second_param) (it doesn't matter the name just the order).

 In Core::handle()
 ............
 $matcher = new UrlMatcher($this->routes, $context);
       
        try {
            $attributes = $matcher->match($request->getPathInfo());
           
            $controller = $attributes['controller'];
           
            unset($attributes['controller']);
           
            $response = call_user_func_array($controller,$attributes);
 .....


 ---------------------------------------------------------------------------------------------------------------
 -------------------------- Basic  templates --------------------------------------------------------------------------
 ----------------------------------------------------------------------------------------------------------------
 under CPANA\Views added a folder called "shared" which should hold the general template of the website
 -added template.php and dumped HTML code inside
 -under CPANA\Views added view class called HomeView.php which contains one method render()

 public function render($msg)
    {   
        $content='';
       
        ob_start();
        $message=$msg;
        require 'shared/template.php';
        $content = ob_get_contents();
        ob_end_clean();
       
        return $content;
    }


 In the controller associated with '/' path (Pages::Home) I added:

 public static function Home()
    {   
        $msg="Pagina de bun venit"; //some content for home page
        $r=new Response();
        $content=new HomeView(); //I create an instance of the view
       
        $r->setContent($content->render($msg));
        return $r;
    }

--------------------------------------------------------------------------------------------------------------
---------- Symfony templating -------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------

Nice! I have now a template an a View class. Let's see how Symfony implements this features:

http://symfony.com/doc/current/components/templating/introduction.html

change content of the HomeView->render() method using the example from documentation:

use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\FilesystemLoader;

class HomeView
{
    public function render($msg)
    {   
   
        $loader = new FilesystemLoader(__DIR__.'/shared/%name%');
        $templating = new PhpEngine(new TemplateNameParser(), $loader);
       
        $content=$templating->render('template.php', array('message' => $msg));
       
        return $content;


It makes sense to move this logic in the Controller and for the moment we can delete HomeView.php, AboutView.php etc.
The controller will look like this:

class Pages
{
    public static function Home()
    {   
        $msg="Home page welcome";
        $r=new Response();
       
        $loader = new FilesystemLoader(dirname(__DIR__).'/Views/shared/%name%');
        $templating = new PhpEngine(new TemplateNameParser(), $loader);
       
        $content=$templating->render('template.php', array('message' => $msg));
               
        $r->setContent($content);
        return $r;
    }

--------------------------------------------------------------------
https://symfony-docs-chs.readthedocs.org/en/2.0/components/templating.html
http://symfony.com/doc/current/components/templating/helpers/slotshelper.html
- add   use Symfony\Component\Templating\Helper\SlotsHelper;
- delete folder shared add both template.php and home.view.php directly under /Views

I am storing a page title and a page content in $msg;
Following the instructions found in the 2 links above I am creating a FilesystemLoader and new PhpEngine object, also add SlotsHelper.
I am not using it yet, but there is also an AssetHelper (http://symfony.com/doc/current/components/templating/helpers/assetshelper.html)

public static function Home()
    {   
        $msg['content']="Pagina de bun venit";
        $msg['title']="Titlu baa";
        $r=new Response();
       
        //use symfony/templeting classes
        $loader = new FilesystemLoader(dirname(__DIR__).'/Views/%name%');
        $templating = new PhpEngine(new TemplateNameParser(), $loader);
        $templating->set(new SlotsHelper());

        // Retrieve $page object
        $content=$templating->render('home.view.php', array('page' => $msg));
               
        $r->setContent($content);
        return $r;
    }


The general template file is "template.php". I put inside the HTML body this code:
<?php
        // The _content slot is a special slot set by the PhpEngine. It contains the content of the subtemplate.
        $view['slots']->output('_content');
?>


The specific page view for path '/' is "home.view.php"
The extend() method is called in the sub-template to set its parent template. Then $view['slots']->set() can be used to set the content of a slot. All content which is not explicitly set in a slot is in the _content slot.

home.view.php

<?php $view->extend('template.php') ?>

<?php $view['slots']->set('title', $page['title']) ?>

<h1>
    <?php echo $page['content'] ?>
</h1>


----------------------------------------------------------------------------------------
---------------------------------  Bootstrap - font end framework ---------------
----------------------------------------------------------------------------------------
 Bootstrap is a free and open-source collection of tools for creating websites and web applications. It contains HTML- and CSS-based  design templates for typography, forms, buttons, navigation and other interface components, as well as optional JavaScript extensions.
It aims to ease the development of dynamic websites and web applications.

Bootstrap is a front end framework, that is, an interface for the user, unlike the server-side code which resides on the "back end" or server.

 I am inspired by this Laravel intro tutorial about I talked in a previous post: http://learninglaravel.net/laravel5/building-our-first-website

 Go to:  http://getbootstrap.com/getting-started

Click Download Bootstrap to download latest compiled Bootstrap files.

Uncompress the downloaded .zip file. We have three folders:

    css
    js
    fonts

Put them all into the public folder of your app
In the <head> of the main template file the following should be added in order to use Bootstrap framework features:

    <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css" >
    <link rel="stylesheet" href="/css/bootstrap-theme.min.css">

    <script src="/js/jquery-1.11.3.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>


Under /Views create a file called "master.template.php" as you can guess this will be the template used by the entire website.
Each view will extend this master template. The master.template.php is containing navigation bar with several buttons and php code to include sub-templates same as we had inside "template.php":

<?php
    // The _content slot is a special slot set by the PhpEngine. It contains the content of the subtemplate.
    $view['slots']->output('_content');
?>


Download the entire code source and review these files.

each view will have a name like "name_of_the_view.view.php"
for example new home.view.php:

<?php $view->extend('master.template.php') ?>

<?php $view['slots']->set('title', $page['title']) ?>

    <div class="container">
        <div class="row banner">

            <div class="col-md-12">

                <h1 class="text-center margin-top-100 editContent">
                 
                </h1>
               
                <h3 class="text-center margin-top-100 editContent"><?php echo $page['content'] ?></h3>

               

            </div>

        </div>
    </div>


-----------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
-----------------------------------------  models  ---------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------
Create DB from phpMyAdmin
framework

user  framework
pass  1234
-----------
created a table called "test" with fields: id, user, pass
add some rows manually

-------
add in index.php a path to Pages:Users     

$app->map('/users','CPANA\Controllers\Pages::Users');

add the method Users in the Pages class

public static function Users()
    {   
        $model=new UsersModel();
        $msg=$model->getUsers();
        .....
        $content=$templating->render('users.view.php', array('page' => $msg,'title' => 'Users'));

----------------------
In here we create a new instance of the model (we will create UsersModel class next).
Use the method getUsers() from Model to retrieve the users.

Send to View the array containing data from DB ('page'=>$msg) and add a new variable 'title' to store the title.
-----------------------------------------
Let's have a look at at our first model under CPANA\Models\UserModel
In constructor we initialize the connection using PDO. This should be moved to an external class to handle the connection, maybe static one.
use \PDO;

class UsersModel
{   
    public  $db;
    public function __construct()
    {   
        $user='framework';
        $pass='1234';
        $this->db = new PDO('mysql:host=localhost;dbname=framework', $user, $pass);
        $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }
   
    public function getUsers()
    {   
        try {
            $stmt = $this->db->query('SELECT * FROM test');
            $results = $stmt->fetchAll(PDO::FETCH_ASSOC);   
        } catch(PDOException $ex) {
            $results="There was an error when connecting to the database";
        }
        return $results;       
    }


This was a good exercise for getting to know individual components from Symfony framework. In order to obtain a working website it should be further developed by adding forms, validation and security components.

The code is available at this link: https://drive.google.com/file/d/0B4lszAGYHn-dMFJqN0lwYUdWeDg/view?usp=sharing
  

Tuesday, August 18, 2015

Pimple dependency injector container. Lambdas and Closures

As you probably saw in a previous post a give it a try on Laravel and I felt is too high level at this moment and I should learn more about the magic behind scene not just how to use a certain framework.
So I looked for a microframework hoping to be easier to understand and I discovered Silex  from the guy who made Symfony: "Silex is a PHP microframework for PHP. It is built on the shoulders of Symfony2 and Pimple and also inspired by sinatra." Next question what is Pimple?
Answer: Pimple is a small Dependency Injection Container for PHP. ( http://pimple.sensiolabs.org/ )

I have no idea about Dependency Injectors, but  found some good materials about Dependency Injection and Pimple here:

http://www.sitepoint.com/dependency-injection-with-pimple

http://fabien.potencier.org/do-you-need-a-dependency-injection-container.html

http://www.slideshare.net/fabpot/dependency-injection-in-php-5354 


Some key ideas from the Fabien's slides:


A Dependency Injector Container/ Service Container manages your SERVICES
Good rule of thumb: It manages "global" objects (objects with only one instance !=Singleton)
Example of such objects: a User, a Request, a database Connetcion, a Logger
Unlike Model objects (a Product, a blog Post)

Dependency Injection Container is simply a PHP object that manages instantiation of other objects. we call them services.


Before starting to read these articles I have to tell you that the actual implementation is based on lambdas and closures. You should have a look before on the following links to get the idea behind this PHP features:

http://culttt.com/2013/03/25/what-are-php-lambdas-and-closures/


About closure binding here:  https://www.christophh.net/2011/10/26/closure-object-binding-in-php-54/
From this article http://programmingarehard.com/2013/10/16/we-need-some-closure.html/ :

"Nowadays, it's very common to see Closures being used for containers. These containers' purpose is to create objects and inject their dependencies so we don't have to do that every single time. Consider the following example.

$container = [];

$container['EmailTemplate'] = function(){

    return new EmailTemplate;
};

$container['DataSource'] = function(){

    return new MySQLDataSource;
};

$container['NewsLetterSender'] = function() use ($container) {

    //used to created emails
    $template   = $container['EmailTemplate']();

    //used to track stats about the sending
    $dataSource = $container['DataSource']();

    return new NewsLetterSender($dataSource, $template);
};

$newsLetterSender = $container['NewsLetterSender']();

//versus the more verbose and less flexible

$newsLetterSender = new NewsLetterSender(new EmailTemplate, new MySQLDataSource);
 
As you can see, the $container's only responsibility is to create and assemble new objects. This concept is known as Inversion of Control. It's so popular because it's an elegant way to encourage dependency injection."




Sunday, August 16, 2015

Joomla componenet: Article generator from RSS feeds - part 4

 My friend who came with the request for this Joomla component gave me some feedback:

  1. - you should be able to split the article into an Intro and Fulltext as some RSS Feeds return large articles
  2.  - the photos should be saved locally (it's good for SEO)
  3. - some RSS feeds contain unwanted links, maybe do something about that also


1.    Something that I learned about articles in Joomla while researching for this task is that the content of the intro and the so called full text are stored in two different columns of the database. It is not supposed to have in Fulltext column also the intro part, only the continuation. Depending on your settings, when you click "Read more" you will be redirected to a page where you will see only the continuation or a concatenated version of Introtext and Fulltext. The configuration is done from:

System ->Global configuration ->Articles ->Show Intro Text set to Show or Hide

I added the logic in the RSSReader.php file found under /models. There is a new function called "htmlParser" which do the magic by calling some other functions:

/**
 * This function identifies images in HTML, download them locally and replace links to external images
    * with links to the local copy. Also splits article into Intro and Fulltext
    *
    * @param string $htmlInput  - string containing HTML to be parsed
    * @param integer $allow_links  - if 0 delete links, if 1 allow links
    * @return array $article_array, $article_array[0] contains introtext, $article_array[1] contains fulltext
    */

    public function htmlParser($htmlInput,$allow_links,$split_after_x)


I searched the internet for a function who is able to split the text containing HTML without breaking any tags. I found one from from CakePHP framwork text helpers via this blog: https://dodona.wordpress.com/2009/04/05/how-do-i-truncate-an-html-string-without-breaking-the-html-code/


2. For saving the photos locally I created a method called getImage which is called by htmlParser.
I am saving the images at this path:  administrator\components\com_rssaggregator\assets\images

I use pathinfo() to get information about actual image file from the link. I also add a random string to the name to be sure is not overwriting some other image with the same name.

public function getImage($link)
    {
        //assume initially that downloading operation is unsuccessfully
        $flag_success=false;
       
             
        $local_path= JPATH_BASE.DS.'administrator'.DS.'components'.DS.'com_rssaggregator'.DS.'assets'.DS.'images'.DS;
       
        //get information from path using pathinfo
        $path_parts = pathinfo($link);
       
        //if file has no extension consider default to be .gif
        if(isset($path_info['extension'])){
            $extension = '.' . substr($path_parts['extension'],0,strpos($path_parts['extension'],'?'));
        }else {
            $extension = '.gif';
        }
       
        //name of the file without extension
        $base_name = $path_parts['filename'];
       
        //local file name will be source file name + random string in order to avoid replacing an image with same name + extension
        $local_file_name= $base_name . '_' . $this->generateRandomString(10). $extension;
       
        $complete_local_path = $local_path . $local_file_name;
        $local_link='/administrator/components/com_rssaggregator/assets/images/'.$local_file_name;
       
        // test for success
        if (copy($link, $complete_local_path)) {
            $flag_success=true;
        }
           
           
        if ($flag_success===true) {
            return $local_link;
        } else {
           return false;
        }
    }



3. I concluded that is difficult to say if the links found in a RSS Feed article are good or bad so I let the Joomla admin to decide if he want to allow them as they are or replace the href value with '#'. This will make them point to the start of the article.

if ($allow_links===0) {
           
            $DOM2 = new DOMDocument;
            $DOM2->loadHTML($editedHTML);

            //get all anchors and change them to #
            $items = $DOM2->getElementsByTagName('a');
      
            foreach($items as $item){
                if($item->hasAttribute('href')) {
                    $item->setAttribute('href','#'); 
                }
            }
            //save changes made to $editedHTML
            $editedHTML=$DOM2->saveHTML();
           
        }


Fork me on GitHub!
 
I have added this project on GitHub, you can download the code from here https://github.com/cristianpana86/com_rssaggregator

Thursday, August 13, 2015

Laravel beginner tutorial. Traits and facades

Start working with Laravel:

Laravel is offering an already configured virtual machine with everything is needed to learn it, but  for some reason you may not be able to use a virtualized environment (I am in this situation). So I will install Laravel on Windows in my EasyPHP Devserver 14.1 VC11 with PHP 6.

Installing Laravel on Windows
-following on the official documentation found here: http://laravel.com/docs/5.1/installation#basic-configuration
-composer command:

    composer create-project laravel/laravel "C:\...your preffered installation path"

After this, create a virtual host which is pointing to "public" folder under root Laravel project path.
I've heard good things about video tutorials on Laracasts but I prefer a written one.I found the site learninglaravel.com which gives you access for free at most of the content (registration needed):

1. basic routing, controllers and views:  http://learninglaravel.net/laravel5/building-our-first-website
2. authentication   -  some part of the authentication tutorial si available only for paid user so I had to figure it out myself using this tutorial:

      https://laraveltips.wordpress.com/2015/06/15/how-to-make-user-login-and-registration-laravel-5-1
and official documentation at: http://laravel.com/docs/5.1/authentication#included-authenticating

Traits:

In the Authentication process  are used traits. Is the first time when I used the concept so I did a little bit of research:
From PHP manual:   As of PHP 5.4.0, PHP implements a method of code reuse called Traits. 

Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. The semantics of the combination of Traits and classes is defined in a way which reduces complexity, and avoids the typical problems associated with multiple inheritance and Mixins. 

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance. 

Facades:

From http://laravel.com/docs/5.0/facades 
Facades provide a "static" interface to classes that are available in the application's service container. Laravel ships with many facades, and you have probably been using them without even knowing it! Laravel "facades" serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.


The Laravel facades work after an intricate logic. Probably the best article I found on this subject is found here:   http://alanstorm.com/laravel_facades

Friday, August 7, 2015

Joomla componenet: Article generator from RSS feeds - part 3

This project (as many others) started just like a "..let's try it" so it suffering from the ugly title disease :) (remember myFrontController which is now a small cms which started a  test for Front Controller design pattern). So I had correct the title to "com_rssaggregator"

Now going back to the functionality, in the cron.rssaggregator.php I instantiate the RSSReader and retrieve the result into an array:

$list_of_feed_articles=$rss->getContent($model->feedsList,$model->noOfFeeds,$model->show_graphic);

What will be now saved in the database looks like this:
$tobind = array(
                           "title" => $feed_article['title'],
                           "alias" => JFilterOutput::stringURLUnicodeSlug($feed_article['title']),
                           "introtext" => $feed_article['desc'],
                           "state"=>'1',
                           "created"=>JFactory::getDate()->toSql(),
                           "featured"=>$featured,
                           "created_by"=>$author,
                           "language"=>'*',
                           "catid" => $category,
                           "metadata"=>'{"page_title":"","author":"","robots":""}',
                        );


- alias - is the slug for each article
- state - state of an article in Joomla com_content component. 1 means published, 0 unpublished
- featured - an article can be also marked as featured (that little star) in the administrator area next to article names
- created_by  - is the ID of the author
- cadid is the ID of the category in which the articles will be published.

In the RSSReader.php you can find the logic for reading the feed and parsing it according to the needs. I tried to  apply the 20/80 rules, meaning that with 20% of effort be able to read image from 80% of the RSS/Atom feeds formats.  I am checking now for the following tags:

<enclosure
<image
<media:content
<media:thumbnail

Also I added the method articleExists($slug) to the model (file models/rssaggreggator.php ). Checks the database for an article with same alias (slug), if there is one already with the same name do not added it again to the table #__content.

As this script will be executed from cron it would be nice to log all the eventual issues that the script encountered in the execution. I made a  logging errors system inspired from here:  http://joomla.stackexchange.com/questions/7286/logging-to-a-file-only-with-jlog

If the log file older than 48 hours, delete it order too prevent having large log files:

if(file_exists($filename)){
    if(time() - filectime($filename) >  48* 3600) unlink($filename);
}


Please go ahead and DOWNLOAD  this beautiful component and give it a try on your own Joomla! website. Any feedback is welcomed.

How to use it:
1.Step 1 install the component
2. In your administrator dashboard look for RSS Aggregator ->Sources view
3. Add at list one valid RSS feed
4. run cron.rssaggregator.com (if you allow direct access to your files which is very bad practice, you write in the browser the path to your file, something like this: http://127.0.0.1/joomla1/administrator/components/com_rssaggregator/cron.rssaggregator.php). Otherwise you can use a command line and execute it as any php file.
5. add the script in cron to be executed recursively, depending on how often the content of the RSS is refreshed.


Saturday, August 1, 2015

Joomla componenet: Article generator from RSS feeds - part 2

So what I want to obtain is to add from time to time articles to the database from those RSS Feeds already added in the Database.
There is a need for a script which should be scheduled after the component installation (using crontab or Windows scheduler depending on the OS)

I will place this php script under /administrator/components/com_rssagregator/ in the Joomla installation folder, and call it "cron.rssagregator.php"

At the beginning I am defining the correct JPATH_BASE from the current file location and include standard Joomla files:

    //basic to make J! happy
    define('_JEXEC', 1); //make j! happy
    define('JPATH_BASE', realpath(substr(dirname(__FILE__),0,strpos(dirname(__FILE__),'administrator'))));;
    define('DS', DIRECTORY_SEPARATOR);


    // Load up the standard stuff for testing
    require_once JPATH_BASE.DS.'includes'.DS.'defines.php';
    require_once JPATH_BASE.DS.'includes'.DS.'framework.php';

I found this information on this link: https://docs.joomla.org/Framework_Compatibility

Further an instance of JTable class is created, not before specifying the path to the specific JTable child class:
    JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_rssagregator/tables');
    $article = JTable::getInstance('content','RssagregatorTable',array());

At the specified path: '/components/com_rssagregator/tables' there is a file called content.php containing a class inheriting JTable:

     class RssagregatorTablecontent extends JTable
    {
        public function __construct($db)
        {
            parent::__construct( '#__content', 'id', $db );
        }   
    }


#__content is the name of the table where the Joomla built in component "com_content" is storing the articles.

You may want to have look on the JTable class documentation here: https://api.joomla.org/cms-3/classes/JTable.html#method_getInstance
For example the JTable::getInstance method:

        getInstance

        Static method to get an instance of a JTable class if it can be found in the table include paths. To add include paths for searching for JTable classes see JTable::addIncludePath().

        getInstance(string $type, string $prefix = 'JTable', array $config = array()) : \JTable|boolean

You can have a look using phpMyAdmin on the #__content table to see the columns names. My component will add values to some of them:

    //values to be saved in the database. this hardcoded values should be replaced with data from RSS feeds  
     $tobind = array(
                    "title" => "articol de test-3",
                   "alias" => "articol de test",
                   "introtext" => "BlueBerry Pie afadf afadfadfa faf a fadfa fad fafafdafa",
                   "state"=>'1',
                   "featured"=>'1',
                   "created_by"=>'453',
                   "language"=>'*',
                   "catid" => "2",
                   "metadata"=>'{"page_title":"","author":"","robots":""}',
                );


Using the JTable::save method the information is saved on the database:

    //if new article successfully added to database echo success message
    if ($article->save($tobind)) {
        echo "The article has been added to database. ";
    } else {
        echo "There was an error. ";
    }


Testing the script is done by directly accessing it from browser: http://joomla1/administrator/components/com_rssagregator/cron.rssagregator.php
"joomla1" is the name of my virtual host. If not using virtual host feature the link looks like:
http://127.0.0.1/joomla1/administrator/components/com_rssagregator/cron.rssagregator.php

From your Joomla administrator, under Content menu, Article manager you can see all the articles including the one newly added.


There is an issue, that even the articles are featured ("featured"=>'1') they do not appear in the list of Featured articles (menu Content -> Featured Articles ).


To solve this we need to add the ID of the new added article in the table #__content_frontpage. A new JTable child class is added in the '/components/com_rssagregator/tables' folder to handle the connection:

        class RssagregatorTablefeatureditems extends JTable
        {
            public function __construct($db)
            {
                parent::__construct( '#__content_frontpage', 'id', $db );
            }
        }


And in the cron.rssagregator.php I will add:

    $article_featured = JTable::getInstance('featureditems','RssagregatorTable',array());

    $tobind = array(
                    "content_id" => $last_id,
                  
                );
               
    if ($article_featured->save($tobind)) {
        echo "new featured article added to database. ";
    } else {
        echo "There was an error on getting a new featured article. ";
    }
   

Of course these error/successes messages are just for testing, later should be changed with some code writing on log files.

The 3 files presented in this blog post:  cron.rssagregator.php, content.php and featureditems.php are available for downloading here. They should be copied in the correct path presented previously.

Joomla componenet: Article generator from RSS feeds - part 1

     I took a break from the "myFrontController" project. A friend of mine who is using Joomla to develop some websites told me "There was a good free extension called Feedgator which
is not developed anymore. Why don't you program something like that?". Basically he needs a Joomla component able to generate articles in the database from some RSS feeds.

    I started reading about Joomla component development with the tutorial from the Joomla! official documentation: J3.x:Developing_an_MVC_Component which can be found at this link:
https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Introduction


It's a good point to start, but is not talking too much about the magic that happens behind. I found some explanations on stackoverflow:

From http://stackoverflow.com/questions/9573570/joomla-getitems-and-how-it-works
------------------------------------------------------------------------
Q:      I am looking at line 34 of /administrator/components/com_contact/views/contacts/view.html.php where is says $this->items = $this->get('Items'); What I don't understand is how that is actually calling the protected function getListQuery() on line 123 of /administrator/components/com_contact/models/contacts.php
There are also some other things I don't understand how are working... like
$this->pagination   = $this->get('Pagination');
$this->state        = $this->get('State');
What are these calling? I looked at the documentation for "get()" but it doesn't say what these are actually calling because I don't see any methods called getPagination, getState or getItems... It appears the get('Items') is somehow magically calling getListQuery().
------------------------------------------------------------------------
A:     I'm presuming 1.7/2.5+ here...
In Joomla!'s MVC the view contacts (ContactViewContacts which extends JView) automatically loads the model contacts (or in J! terminology ContactModelContacts) which as a class extends JModelList.
The get() looks in the view to get data from a registered model or a property of the view.
So:
    $this->items = $this->get('Items');
is actually a call to the model ContactModelContacts which has a matching getItems() in it's parent.
The model file com_contact/models/contacts.php doesn't implement it's own getItems(), so the getItems() from the JModelList class is used (found in /libraries/joomla/application/component/modellist.php).
This in turn calls getListQuery() - no magic just inheritance.
The $this->get('Pagination') is doing the same thing, ie. accessing the implementation in the models parent.
The $this->get('State') is probably going all the way back to the JModel implementation.
------------------------------------------------------------------------

Some other links from where I tried to understand how the Joomla component works:





  
       In order to accelerate the process and create all the files structure I used the free service from: http://www.component-creator.com (I would not called it  cheating, rather increasing productivity)
and generated a Joomla component. This component has just some backend logic and one DB table. There is a view from where you can enter  RSS feed linka, name and  number of posts.

To this structure I added in the "/site" part of the component, the logic for reading the RSS feeds and just display them.

I added a simple view which contains a display() method  :

/com_rssagregator/site/views/rssagregator/view.html.php

            function display($tpl = null)
            {
                // Assign data to the view
                $this->msg = $this->get('Msg');
               
                // Check for errors.
                if (count($errors = $this->get('Errors')))
                {
                    JLog::add(implode('<br />', $errors), JLog::WARNING, 'jerror');
       
                    return false;
                }
       
                // Display the view
                parent::display($tpl);
            }


In the corresponded model class I implemented the getMsg() method.
 /com_rssagregator/site/models/rssagregator.php.
The logic is :
1. read the list of feeds from the database (which were previously added in back end - administrator view)
2. read the the number of posts to be displayed for each feed from the database (which also were previously added in back end - administrator view)
3. require_once the "RSSReader.php" file in which I implemented the logic for reading feed posts
4. call getContent($feedsList,$noOfFeeds);
5. in the RSSReader.php each feed is read entirely and then only the first "noOfPosts" are returned in the output. The output is formatted and ready to be sent to the view for displaying it.

    public function parseData($feedObj,$limit)
    {
   
        $rss = new DOMDocument();
        $rss->loadXML($feedObj);
       
        $feed = array();
        foreach ($rss->getElementsByTagName('item') as $node) {
            $item = array (
                'title' => $node->getElementsByTagName('title')->item(0)->nodeValue,
                'desc' => $node->getElementsByTagName('description')->item(0)->nodeValue,
                'link' => $node->getElementsByTagName('link')->item(0)->nodeValue,
                'date' => $node->getElementsByTagName('pubDate')->item(0)->nodeValue,
                );
            array_push($feed, $item);
        }
       
        $output='';
        for($x=0;$x<$limit;$x++) {
            $title = str_replace(' & ', ' &amp; ', $feed[$x]['title']);
            $link = $feed[$x]['link'];
            $description = $feed[$x]['desc'];
            $date = date('l F d, Y', strtotime($feed[$x]['date']));
            ///////////////////////////////////////////////////////////
           
            $output.= '<p><strong><a href="'.$link.'" title="'.$title.'">'.$title.'</a></strong><br />';
            $output.= '<small><em>Posted on '.$date.'</em></small></p>';
            $output.= '<p>'.$description.'</p><br/><br/>';
           
           
        }
        return $output;
    }

6. In order to visualize the result, from backend add a menu item pointing to this newly created view. This is possible only if
in the "view/<view_name>/tmpl/" exists a an xml file contianing a layout title and description like this one:

    <?xml version="1.0" encoding="utf-8"?>
    <metadata>
        <layout title="COM_RSSAGREGATOR_CPANARSS_VIEW_DEFAULT">
            <message>link menu catre RSSAGREGATOR default view</message>
        </layout>
    </metadata>

   
The result of my work can be downloaded from here.

Instead of directly displaying the feed posts this  component should be further developed to add the feed posts to the database as articles.