Zip parsing kinda works. Seems to be an issue with packs. Committing so I can work...
authorCameron Ball <cameron@getapproved.com.au>
Fri, 21 Nov 2014 08:59:32 +0000 (16:59 +0800)
committerCameron Ball <cameron@getapproved.com.au>
Fri, 21 Nov 2014 08:59:32 +0000 (16:59 +0800)
26 files changed:
Controllers/PackTestController.php [new file with mode: 0644]
Controllers/SimfileController.php
DataAccess/DataMapper/DataMapper.php
DataAccess/DataMapper/Helpers/AbstractPopulationHelper.php
DataAccess/DataMapper/Helpers/EntityArrayMapsHelper.php [new file with mode: 0644]
DataAccess/FileRepository.php
DataAccess/StepMania/IPackRepository.php [new file with mode: 0644]
DataAccess/StepMania/PackRepository.php [new file with mode: 0644]
DataAccess/StepMania/SimfileRepository.php
DataAccess/functions.php
Domain/Entities/StepMania/IPack.php
Domain/Entities/StepMania/IPackBuilder.php
Domain/Entities/StepMania/Pack.php
Domain/Entities/StepMania/PackBuilder.php
Domain/Entities/StepMania/PackFactory.php
Domain/Entities/StepMania/PackStepByStepBuilder.php
Domain/Entities/StepMania/SimfileStepByStepBuilder.php
Services/BannerExtracter.php
Services/IBannerExtracter.php
Services/IZipParser.php [new file with mode: 0644]
Services/SimfileParser.php
Services/Uploads/UploadManager.php
Services/ZipParser.php [new file with mode: 0644]
config/DI.php
config/DataMaps.php
config/Routes.php

diff --git a/Controllers/PackTestController.php b/Controllers/PackTestController.php
new file mode 100644 (file)
index 0000000..656f9f9
--- /dev/null
@@ -0,0 +1,26 @@
+<?php\r
+\r
+namespace Controllers;\r
+\r
+use Controllers\IDivineController;\r
+use DataAccess\StepMania\IPackRepository;\r
+use DataAccess\StepMania\ISimfileRepository;\r
+\r
+class PackTestController implements IDivineController\r
+{\r
+    private $_packRepository;\r
+    \r
+    public function __construct(\r
+        IPackRepository $repository\r
+    ) {\r
+        $this->_packRepository = $repository;\r
+    }\r
+    \r
+    public function indexAction() {\r
+        $pack = $this->_packRepository->findById(1);\r
+\r
+        echo '<pre>';\r
+        print_r($pack);\r
+        echo '</pre>';\r
+    }\r
+}\r
index 47ffd7b..00dc15c 100644 (file)
@@ -2,44 +2,41 @@
 \r
 namespace Controllers;\r
 \r
-use ZipArchive;\r
-use Exception;\r
 use Controllers\IDivineController;\r
 use Services\Http\IHttpResponse;\r
 use Services\Uploads\IUploadManager;\r
-use Services\ISimfileParser;\r
 use Services\IUserSession;\r
-use Services\IBannerExtracter;\r
+use Services\IZipParser;\r
 use DataAccess\StepMania\ISimfileRepository;\r
-use Domain\Entities\StepMania\ISimfileStepByStepBuilder;\r
+use DataAccess\StepMania\IPackRepository;\r
+use DataAccess\IFileRepository;\r
 \r
 class SimfileController implements IDivineController\r
 {\r
     private $_simfileRepository;\r
+    private $_packRepository;\r
+    private $_fileRepository;\r
     private $_response;\r
     private $_uploadManager;\r
-    private $_simfileParser;\r
-    private $_simfileBuilder;\r
-    private $_userRepository;\r
     private $_userSession;\r
-    private $_bannerExtracter;\r
+    private $_zipParser;\r
     \r
     public function __construct(\r
         IHttpResponse $response,\r
         IUploadManager $uploadManager,\r
-        ISimfileRepository $repository,\r
+        ISimfileRepository $simfileRepository,\r
+        IPackRepository $packRepository,\r
+        IFileRepository $fileRepository,\r
         IUserSession $userSession,\r
-        ISimfileParser $simfileParser,\r
-        ISimfileStepByStepBuilder $simfileBuilder,\r
-        IBannerExtracter $bannerExtracter\r
+        IZipParser $zipParser\r
     ) {\r
         $this->_response = $response;\r
         $this->_uploadManager = $uploadManager;\r
-        $this->_simfileRepository = $repository;\r
+        $this->_simfileRepository = $simfileRepository;\r
+        $this->_packRepository = $packRepository;\r
+        $this->_fileRepository = $fileRepository;\r
         $this->_userSession = $userSession;\r
-        $this->_simfileParser = $simfileParser;\r
-        $this->_simfileBuilder = $simfileBuilder;\r
-        $this->_bannerExtracter = $bannerExtracter;\r
+        $this->_zipParser = $zipParser;\r
     }\r
     \r
     public function indexAction() {\r
@@ -102,49 +99,24 @@ class SimfileController implements IDivineController
 \r
         foreach($files as $file)\r
         {\r
-            $za = new ZipArchive();\r
-            //XXX: We assume all files are zips. Should be enforced by validation elsewhere.\r
-            $res = $za->open('../files/StepMania/' . $file->getHash() . '.zip');\r
+            $zipParser = $this->_zipParser;\r
+            $zipParser->parse($file);\r
             \r
-            if($res !== true) throw new Exception ('Could not open zip for reading.');\r
-            \r
-            for($i=0; $i<$za->numFiles; $i++)\r
+            //save the actual zip in the db\r
+            //$this->_fileRepository->save($file);  \r
+            foreach($zipParser->simfiles() as $simfile)\r
             {\r
-                $stat = $za->statIndex($i);\r
-                if(pathinfo($stat['name'], PATHINFO_EXTENSION) == 'sm')\r
-                {\r
-                    $smData = file_get_contents('zip://../files/StepMania/' . $file->getHash() . '.zip#' . $stat['name']);\r
-                    break;\r
-                }\r
+                $this->_fileRepository->save($simfile->getBanner());\r
+                $this->_fileRepository->save($simfile->getSimfile());\r
+                $this->_simfileRepository->save($simfile);\r
             }\r
-\r
-            if(!$smData) throw new Exception('Could not extract simfile.');\r
-\r
-            /* @var $parser \Services\ISimfileParser */\r
-            $parser = $this->_simfileParser;\r
-            $parser->parse($smData);\r
-\r
-            $banner = $this->_bannerExtracter->extractBanner('../files/StepMania/' . $file->getHash() . '.zip', $parser->banner());\r
-                        \r
-            //TODO: Create file object for banner and .zip then link them up\r
-            //shouldn't need to use repository as the mapper can create the db entries\r
-            //all in one go (I think ...)\r
-            //\r
-            //Need to make FileBuilder and FileStepByStepBuilder\r
-            $simfile = $this->_simfileBuilder->With_Title($parser->title())\r
-                                             ->With_Artist($parser->artist())\r
-                                             ->With_Uploader($this->_userSession->getCurrentUser()) //obj\r
-                                             ->With_BPM($parser->bpm())\r
-                                             ->With_BpmChanges($parser->bpmChanges())\r
-                                             ->With_Stops($parser->stops())\r
-                                             ->With_FgChanges($parser->fgChanges())\r
-                                             ->With_BgChanges($parser->bgChanges())\r
-                                             ->With_Steps($parser->steps())\r
-                                             ->With_Simfile($file)\r
-                                             ->With_Banner($banner)\r
-                                             ->build();\r
             \r
-            $this->_simfileRepository->save($simfile);\r
+            if($zipParser->isPack())\r
+            {\r
+                $pack = $zipParser->pack();\r
+                $this->_fileRepository->save($pack->getBanner());\r
+                $this->_packRepository->save($pack);\r
+            }\r
         }\r
     }\r
 }\r
index 624e03e..24b7994 100644 (file)
@@ -49,7 +49,7 @@ class DataMapper implements IDataMapper
             $class->setId($row['id']);\r
             $entities[$row['id']] = $class;\r
         }\r
-\r
+        \r
         return $entities;\r
     }\r
         \r
@@ -136,7 +136,6 @@ class DataMapper implements IDataMapper
             foreach($queries as $query)\r
             {\r
                 $query = str_replace('%MAIN_QUERY_ID%', end($idMap), $query);\r
-                echo $query;\r
                 $statement = $this->_db->prepare($query);\r
                 $statement->execute();\r
             }\r
index a3e518b..d32f464 100644 (file)
@@ -2,8 +2,6 @@
 \r
 namespace DataAccess\DataMapper\Helpers;\r
 \r
-use DataAccess\DataMapper\Helpers\VOMapsHelper;\r
-use Domain\Entities\IDivineEntity;\r
 use Exception;\r
 \r
 class AbstractPopulationHelper\r
@@ -18,7 +16,7 @@ class AbstractPopulationHelper
     static function getConstrutorArray($maps, $entity, $row, $db)\r
     {\r
         $constructors = array();\r
-        \r
+                \r
         foreach($maps[$entity]['maps'] as $constructor => $mapsHelper)\r
         {\r
             switch(get_class($mapsHelper))\r
@@ -30,11 +28,11 @@ class AbstractPopulationHelper
                 case 'DataAccess\DataMapper\Helpers\VOMapsHelper':\r
                 case 'DataAccess\DataMapper\Helpers\VOArrayMapsHelper':\r
                 case 'DataAccess\DataMapper\Helpers\EntityMapsHelper':\r
+                case 'DataAccess\DataMapper\Helpers\EntityArrayMapsHelper':\r
                     $constructors[$constructor] = $mapsHelper->populate($maps, $db, $entity, $row);\r
                     break;\r
             }\r
         }\r
-        \r
         return $constructors;\r
     }\r
     \r
@@ -225,18 +223,32 @@ class AbstractPopulationHelper
                     // in the case of setting up a new entity, the VOs should never\r
                     // exist in the first place, so we just make them.\r
                     case 'DataAccess\DataMapper\Helpers\VOArrayMapsHelper':\r
+                    case 'DataAccess\DataMapper\Helpers\EntityArrayMapsHelper':\r
                         if($id && isset($property[0]))\r
                         {\r
                             // If we assume that all elements in the array are the same then\r
                             // we can just use the first one to figure out which maps entry to use\r
 \r
                             $subEntityMapsIndex = self::getMapsNameFromEntityObject($property[0], $maps);\r
+                            //TODO: I think this function will work with Entities too, but I should probably rename it at some point\r
                             $voIds = self::mapVOArrayToIds($maps[$subEntityMapsIndex]['table'],\r
                                 array(strtolower($entityMapsIndex . '_id'), $id),\r
                                 $db);\r
 \r
                             foreach($property as $index => $propertyArrayElement)\r
                             {\r
+                                //XXX: I wanted this to only run on VOs, not entities. But there's a problem with that.\r
+                                //when creating a pack, the simfile entities need to reference the pack, and the only way for\r
+                                //that to happen is here. What I do instead is check that the entity has an id (which implies\r
+                                //it has already been created and save) if it is a IDivineEntity. If it doesn't, complain.\r
+                                //this ensures consistent behaviour with other parts of this mapper.\r
+                                if($property instanceof \Domain\Entities\IDivineEntity && !$property->getId())\r
+                                {\r
+                                    throw new Exception(sprintf(\r
+                                        'Could not find referenced entity, %s, in the database. Has it been saved yet?',\r
+                                         $mapsHelper->getEntityName()));\r
+                                }\r
+                                \r
                                 $extra = array(strtolower($entityMapsIndex . '_id') => $id);\r
                                 if(isset($voIds[$index]))\r
                                 {\r
@@ -250,6 +262,18 @@ class AbstractPopulationHelper
                         } else {\r
                             foreach($property as $propertyArrayElement)\r
                             {\r
+                                //XXX: I wanted this to only run on VOs, not entities. But there's a problem with that.\r
+                                //when creating a pack, the simfile entities need to reference the pack, and the only way for\r
+                                //that to happen is here. What I do instead is check that the entity has an id (which implies\r
+                                //it has already been created and save) if it is a IDivineEntity. If it doesn't, complain.\r
+                                //this ensures consistent behaviour with other parts of this mapper.\r
+                                if($property instanceof \Domain\Entities\IDivineEntity && !$property->getId())\r
+                                {\r
+                                    throw new Exception(sprintf(\r
+                                        'Could not find referenced entity, %s, in the database. Has it been saved yet?',\r
+                                         $mapsHelper->getEntityName()));\r
+                                }\r
+                                \r
                                 // TODO: TRICKY! Since this is a back-reference, it\r
                                 // needs the ID of the object we're trying to save\r
                                 // to complete\r
@@ -260,22 +284,6 @@ class AbstractPopulationHelper
                 }\r
             }\r
         }\r
-//        \r
-//        if($id)\r
-//        {\r
-//            $query = substr($query, 0, -2);\r
-//            $query .= sprintf(' WHERE id=%u', $id);\r
-//            $queries['TYPE'] = self::QUERY_TYPE_UPDATE;\r
-//        } else {\r
-//            $queryColumnNamesAndValues = array_merge($queryColumnNamesAndValues, $extraColumns);\r
-//            $query = sprintf('INSERT INTO %s (%s) VALUES (%s)',\r
-//                $maps[$entityMapsIndex]['table'],\r
-//                implode(', ', array_keys($queryColumnNamesAndValues)),\r
-//                implode(', ', $queryColumnNamesAndValues));\r
-//            $queries['TYPE'] = self::QUERY_TYPE_CREATE;\r
-//        }\r
-//        print_r($queryColumnNamesAndValues);\r
-//        echo '<br />';\r
         \r
         if($id)\r
         {\r
diff --git a/DataAccess/DataMapper/Helpers/EntityArrayMapsHelper.php b/DataAccess/DataMapper/Helpers/EntityArrayMapsHelper.php
new file mode 100644 (file)
index 0000000..21f342e
--- /dev/null
@@ -0,0 +1,82 @@
+<?php\r
+\r
+namespace DataAccess\DataMapper\Helpers;\r
+\r
+use ReflectionClass;\r
+\r
+class EntityArrayMapsHelper\r
+{\r
+    private $_entityName;\r
+    private $_tableName;\r
+    private $_accessor;\r
+    \r
+    public function __construct($entityName, $accessor, $tableName)\r
+    {\r
+        $this->_entityName = $entityName;\r
+        \r
+        if($tableName) {\r
+            $this->_tableName = $tableName;\r
+        } else {\r
+            $this->_tableName = strtolower($entityName);\r
+        }\r
+        \r
+        if($accessor)\r
+        {\r
+            $this->_accessor = $accessor;\r
+        } else\r
+        {\r
+            $this->_accessor = 'get'. str_replace('_', '', $entityName);\r
+        }\r
+    }\r
+    \r
+    public function getEntityName()\r
+    {\r
+        return $this->_entityName;\r
+    }\r
+    \r
+    public function getAccessor()\r
+    {\r
+        return $this->_accessor;\r
+    }\r
+    \r
+    public function getTableName()\r
+    {\r
+        return $this->_tableName;\r
+    }\r
+    \r
+    public function populate($maps, $db, $parent, $row)\r
+    {\r
+        $className = $maps[$this->_entityName]['class'];\r
+        $table = $maps[$this->_entityName]['table'];\r
+        $entityArray = array();\r
+\r
+        // in this case we look in another table for this row's id\r
+        $join_id = $row['id'];        \r
+        $statement = $db->prepare(sprintf('SELECT * from %s WHERE %s=%u',\r
+            $table,\r
+            strtolower($parent . '_id'),\r
+            $join_id\r
+        ));\r
+        \r
+        $statement->execute();\r
+        $rows = $statement->fetchAll();\r
+        \r
+        foreach($rows as $row)\r
+        {         \r
+            $constructors = AbstractPopulationHelper::getConstrutorArray($maps, $this->_entityName, $row, $db);\r
+\r
+            if(count($constructors) == 0)\r
+            {\r
+                $class = new $className;\r
+            } else {\r
+                $r = new ReflectionClass($className);\r
+                $class = $r->newInstanceArgs($constructors);\r
+            }\r
+        \r
+            $class->setId($row['id']);\r
+            $entityArray[] =  $class;\r
+        }\r
+        \r
+        return $entityArray;\r
+    }\r
+}
\ No newline at end of file
index 075b7d0..bba8703 100644 (file)
@@ -5,7 +5,6 @@ namespace DataAccess;
 use DataAccess\IFileRepository;
 use DataAccess\DataMapper\IDataMapper;
 use DataAccess\Queries\IQueryBuilderFactory;
-use Domain\Entities\IFile;
 
 class FileRepository implements IFileRepository
 {
diff --git a/DataAccess/StepMania/IPackRepository.php b/DataAccess/StepMania/IPackRepository.php
new file mode 100644 (file)
index 0000000..c3d29a9
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+namespace DataAccess\StepMania;
+
+use DataAccess\IRepository;
+use Domain\Entities\StepMania\IPack;
+
+interface IPackRepository extends IRepository
+{
+    public function findByTitle($title);
+    public function findByContributor($contributor);
+    public function save(IPack $entity);
+    public function remove(IPack $entity);
+}
+
+    
diff --git a/DataAccess/StepMania/PackRepository.php b/DataAccess/StepMania/PackRepository.php
new file mode 100644 (file)
index 0000000..55fa97c
--- /dev/null
@@ -0,0 +1,64 @@
+<?php\r
+\r
+namespace DataAccess\StepMania;\r
+\r
+use DataAccess\StepMania\IPackRepository;\r
+use DataAccess\DataMapper\IDataMapper;\r
+use DataAccess\Queries\IQueryBuilderFactory;\r
+use Domain\Entities\StepMania\IPack;\r
+\r
+//TODO: Implement some sort of caching. Probably OK for now not to worry.\r
+class PackRepository implements IPackRepository\r
+{\r
+    private $_dataMapper;\r
+    private $_queryBuilderFactory;\r
+    \r
+    public function __construct(IDataMapper $dataMapper, IQueryBuilderFactory $queryBuilderFactory) {\r
+        $this->_dataMapper = $dataMapper;\r
+        $this->_queryBuilderFactory = $queryBuilderFactory;\r
+    }\r
+    \r
+    public function findById($id) {\r
+        $queryBuilder = $this->_queryBuilderFactory->createInstance();\r
+        $queryBuilder->where('id', '=', $id);\r
+        \r
+        $result = $this->_dataMapper->map('Pack', $queryBuilder);\r
+        return reset($result);\r
+    }\r
+    \r
+    public function findRange($id, $limit)\r
+    {\r
+        $queryBuilder = $this->_queryBuilderFactory->createInstance();\r
+        $queryBuilder->where('id', '>=', $id)->limit($limit);\r
+                \r
+        return $this->_dataMapper->map('Pack', $queryBuilder);\r
+    }\r
+    \r
+    public function save(IPack $entity) {\r
+        return $this->_dataMapper->save($entity);\r
+    }\r
+    \r
+    //TODO: Implement\r
+    public function remove(IPack $entity) {\r
+        ;\r
+    }\r
+    \r
+    public function findByTitle($title)\r
+    {\r
+        $queryBuilder = $this->_queryBuilderFactory->createInstance();\r
+        $queryBuilder->where('title', 'LIKE', "%%$title%%");\r
+        \r
+        return $this->_dataMapper->map('Pack', $queryBuilder);\r
+    }\r
+    \r
+    public function findByContributor($artistName)\r
+    {\r
+        $queryBuilder = $this->_queryBuilderFactory->createInstance();\r
+        $queryBuilder->join('inner', 'packs', 'id', 'simfiles', 'pack_id')\r
+                     ->join('inner', 'simfiles', 'id', 'steps', 'simfile_id')\r
+                     ->join('inner', 'steps', 'step_artist_id', 'step_artists', 'id')\r
+                     ->where('tag', 'LIKE', "%%$artistName%%");\r
+        \r
+        return $this->_dataMapper->map('Pack', $queryBuilder);\r
+    }\r
+}\r
index d80efd5..5803c21 100644 (file)
@@ -23,8 +23,9 @@ class SimfileRepository implements ISimfileRepository
     public function findById($id) {\r
         $queryBuilder = $this->_queryBuilderFactory->createInstance();\r
         $queryBuilder->where('id', '=', $id);\r
-                \r
-        return $this->_dataMapper->map('Simfile', $queryBuilder);\r
+             \r
+        $result = $this->_dataMapper->map('Simfile', $queryBuilder);\r
+        return reset($result);\r
     }\r
     \r
     public function findRange($id, $limit)\r
index df43446..82669e9 100644 (file)
@@ -3,6 +3,7 @@
 namespace DataAccess;\r
 \r
 use DataAccess\DataMapper\Helpers\EntityMapsHelper;\r
+use DataAccess\DataMapper\Helpers\EntityArrayMapsHelper;\r
 use DataAccess\DataMapper\Helpers\VOMapsHelper;\r
 use DataAccess\DataMapper\Helpers\VOArrayMapsHelper;\r
 use DataAccess\DataMapper\Helpers\IntMapsHelper;\r
@@ -13,6 +14,11 @@ function Entity($mapName, $accessor=null, $tableName = null)
     return new EntityMapsHelper($mapName, $accessor, $tableName);\r
 }\r
 \r
+function EntityArray($mapName, $accessor=null, $tableName = null)\r
+{\r
+    return new EntityArrayMapsHelper($mapName, $accessor, $tableName);\r
+}\r
+\r
 function VO($mapName, $accessor=null, $tableName = null)\r
 {\r
     return new VOMapsHelper($mapName, $accessor, $tableName);\r
index 0f7eddf..124c776 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-namespace Domain\Entities;
+namespace Domain\Entities\StepMania;
 
 use Domain\Entities\IDivineEntity;
 
@@ -10,5 +10,6 @@ interface IPack extends IDivineEntity
     public function getUploader();
     public function getContributors();
     public function getSimfiles();
+    public function getBanner();
     public function getFile();
 }
index 10e5d6d..d99c53e 100644 (file)
@@ -10,6 +10,7 @@ interface IPackBuilder
     public function With_Title($title);
     public function With_Uploader(IUser $uploader);
     public function With_Simfiles(array $simfiles);
+    public function With_Banner(IFile $banner);
     public function With_File(IFile $file);
     public function build();
 }
\ No newline at end of file
index 970c8df..6df8d8e 100644 (file)
@@ -6,7 +6,7 @@ use Exception;
 use Domain\Entities\StepMania\ISimfile;
 use Domain\Entities\IUser;
 use Domain\Entities\IFile;
-use Domain\Entities\IPack;
+use Domain\Entities\StepMania\IPack;
 use Domain\Entities\AbstractEntity;
 
 class Pack extends AbstractEntity implements IPack
@@ -14,16 +14,19 @@ class Pack extends AbstractEntity implements IPack
     private $_title;
     private $_uploader;
     private $_simfiles;
+    private $_banner;
     private $_file;
     
     public function __construct(
         $title,
         IUser $uploader,
         array $simfiles,
+        IFile $banner = null,
         IFile $file = null
     ) {
         $this->_title = $title;
         $this->_uploader = $uploader;
+        $this->_banner = $banner;
         $this->_file = $file;
         
         foreach($simfiles as $simfile) {
@@ -68,6 +71,11 @@ class Pack extends AbstractEntity implements IPack
         return $this->_uploader;
     }
     
+    public function getBanner()
+    {
+        return $this->_banner;
+    }
+    
     private function getAllStepArtistsFromSimfile(ISimfile $simfile)
     {
         $artists = array();
index e9f1099..3a52a36 100644 (file)
@@ -14,6 +14,7 @@ class PackBuilder implements IPackBuilder
     private $_title;
     private $_uploader;
     private $_simfiles;
+    private $_banner;
     private $_file;
     
     //override parent
@@ -28,6 +29,11 @@ class PackBuilder implements IPackBuilder
         return $this;
     }
     
+    public function With_Banner(IFile $banner)
+    {
+        $this->_banner = $banner;
+    }
+    
     public function With_File(IFile $file)
     {
         $this->_file = $file;
index 6723466..b59c49a 100644 (file)
@@ -11,6 +11,7 @@ interface IPackFactory
         $title,
         IUser $uploader,
         array $simfiles,
+        IFile $banner = null,
         IFile $file = null
     );
 }
@@ -21,12 +22,14 @@ class PackFactory implements IPackFactory
         $title,
         IUser $uploader,
         array $simfiles,
+        IFile $banner = null,
         IFile $file = null
     ) {
         return new Pack(
             $title,
             $uploader,
             $simfiles,
+            $banner,
             $file
         );
     }
index 89261b3..cc53507 100644 (file)
@@ -23,6 +23,7 @@ interface IPackStepByStepBuilder_With_Uploader
 
 interface IPackStepByStepBuilder_With_Simfiles
 {
+    public function With_Banner(IFile $banner);
     public function With_File(IFile $file);
     public function build();
 }
@@ -52,8 +53,8 @@ class PackStepByStepBuilder_With_Title extends AbstractPackStepByStepBuilder imp
 {        
     public function With_Uploader(IUser $user)
     {
-        $this->_packBuilder->With_Artist($artist);
-        return new PackStepByStepBuilder_With_Artist($this->_packBuilder);
+        $this->_packBuilder->With_Uploader($user);
+        return new PackStepByStepBuilder_With_Uploader($this->_packBuilder);
     }
 }
 
@@ -68,11 +69,18 @@ class PackStepByStepBuilder_With_Uploader extends AbstractPackStepByStepBuilder
 
 class PackStepByStepBuilder_With_Simfiles extends AbstractPackStepByStepBuilder implements IPackStepByStepBuilder_With_Simfiles
 {
+    public function With_Banner(IFile $banner)
+    {
+        $this->_packBuilder->With_File($banner);
+        return $this;
+    }
+    
     public function With_File(Ifile $file)
     {
         $this->_packBuilder->With_File($file);
+        return $this;
     }
-    
+
     public function build()
     {
         return $this->_simfileBuilder
index 8f03a2f..d0b9971 100644 (file)
@@ -158,7 +158,7 @@ class SimfileStepByStepBuilder_With_Steps extends AbstractSimfileStepByStepBuild
     public function With_Banner(IFile $banner)\r
     {\r
         $this->_simfileBuilder->With_Banner($banner);\r
-        return new SimfileStepByStepBuilder_With_Steps($this->_simfileBuilder);\r
+        return new SimfileStepByStepBuilder_With_Steps($this->_simfileBuilder); //TODO: Pretty sure return $this will be OK\r
     }\r
     \r
     public function With_Simfile(IFile $simfile)\r
index 843fd8b..1fc49f3 100644 (file)
@@ -6,22 +6,20 @@ use ZipArchive;
 use finfo;
 use Exception;
 use Services\IBannerExtracter;
-use DataAccess\IFileRepository;
 use Domain\Entities\IFileStepByStepBuilder;
 
+//TODO: This class can probably be refactored to be nicer. Also perhaps the methods could be static?
 class BannerExtracter implements IBannerExtracter
 {
     private $_builder;
     private $_destinationFileName;
     private $_hash;
-    private $_fileRepository;
 
-    public function __construct(IFileStepByStepBuilder $builder, IFileRepository $fileRepository) {
+    public function __construct(IFileStepByStepBuilder $builder) {
         $this->_builder = $builder;
-        $this->_fileRepository = $fileRepository;
     }
     
-    public function extractBanner($zipfile, $bannerName) {
+    public function extractSongBanner($zipfile, $bannerName) {
         $za = new ZipArchive();
         //XXX: We assume all files are zips. Should be enforced by validation elsewhere.
         $res = $za->open($zipfile);
@@ -40,21 +38,64 @@ class BannerExtracter implements IBannerExtracter
             }
         }
 
-        if(!isset($result) || !$result) throw new Exception('Could not extract banner.');
+        if(!isset($result) || !$result) return null;
 
         $finfo = new finfo(FILEINFO_MIME);
         $mimetype = $finfo->file('../files/banners/' . $this->_destinationFileName);
         $size = filesize('../files/banners/' . $this->_destinationFileName);
         /* @var $fff \Domain\Entities\FileStepByStepBuilder */
-        $file= $this->_builder->With_Hash($this->_hash)
+        return $this->_builder->With_Hash($this->_hash)
                               ->With_Path('banners')
                               ->With_Filename($bannerName)
                               ->With_Mimetype($mimetype)
                               ->With_Size($size)
                               ->With_UploadDate(time())
                               ->build();
+    }
+    
+    public function extractPackBanner($zipfile, $packname)
+    {
+        $bannerName = '';
+        $za = new ZipArchive();
+        //XXX: We assume all files are zips. Should be enforced by validation elsewhere.
+        $res = $za->open($zipfile);
+
+        if($res !== true) throw new Exception ('Could not open zip for reading.');
+        
+        for($i=0; $i<$za->numFiles; $i++)
+        {
+            $stat = $za->statIndex($i);
+            $type = @exif_imagetype('zip://' . $zipfile . '#' . $stat['name']);
+
+            if($type !== false)
+            {
+                $pathComponents = explode('/',$stat['name']);
+
+                //replace 3spooty with packname variable
+                if(count($pathComponents) == 2 && $pathComponents[0] == $packname)
+                {
+                    $this->_hash = $this->randomFilename($stat['name']);
+                    $this->_destinationFileName = $this->_hash . '.' . pathinfo($stat['name'], PATHINFO_EXTENSION);
+                    $bannerName = $pathComponents[1];
+                    $result = copy('zip://' . $zipfile . '#' . $stat['name'], '../files/banners/' . $this->_destinationFileName);
+                    break;
+                }
+            }
+        }
         
-        return $this->_fileRepository->save($file);
+        if(!isset($result) || !$result) return null;
+
+        $finfo = new finfo(FILEINFO_MIME);
+        $mimetype = $finfo->file('../files/banners/' . $this->_destinationFileName);
+        $size = filesize('../files/banners/' . $this->_destinationFileName);
+        /* @var $fff \Domain\Entities\FileStepByStepBuilder */
+        return $this->_builder->With_Hash($this->_hash)
+                              ->With_Path('banners')
+                              ->With_Filename($bannerName)
+                              ->With_Mimetype($mimetype)
+                              ->With_Size($size)
+                              ->With_UploadDate(time())
+                              ->build();
     }
     
     private function randomFilename($seed)
index 7407367..a56a31f 100644 (file)
@@ -4,5 +4,6 @@ namespace Services;
 
 interface IBannerExtracter
 {
-    public function extractBanner($zipfile, $bannerName);
+    public function extractSongBanner($zipfile, $bannerName);
+    public function extractPackBanner($zipfile, $packname);
 }
diff --git a/Services/IZipParser.php b/Services/IZipParser.php
new file mode 100644 (file)
index 0000000..9b74480
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+namespace Services;
+
+use Domain\Entities\IFile;
+
+interface IZipParser
+{
+    public function parse(IFile $zipFile);
+    public function isPack();
+    public function isSingle();
+    public function pack();
+    public function simfiles();
+}
\ No newline at end of file
index 161db7d..bc75ba0 100644 (file)
@@ -20,7 +20,6 @@ class SimfileParser implements ISimfileParser
         
     public function parse($simfileData)
     {
-        //XXX: Should I explode on ';' instead? That seems like it might be a more reliable delimiter
         $this->_smFileLines = explode(";", $simfileData);
     }
     
index 0bfa03e..7f381c1 100644 (file)
@@ -94,15 +94,13 @@ class UploadManager implements IUploadManager{
             $hash = $this->saveFile($file);
             
             /* @var $file \Services\Uploads\IFile */
-            $file = $this->_fileBuilder->With_Hash($hash)
+            $results[] = $this->_fileBuilder->With_Hash($hash)
                                        ->With_Path(rtrim($this->_destination, '/'))
                                        ->With_Filename($file->getName())
                                        ->With_Mimetype($file->getType())
                                        ->With_Size($file->getSize())
                                        ->With_UploadDate(time())
                                        ->build();
-            
-            $results[] = $this->_fileRepository->save($file);
         }
         
         return $results;
diff --git a/Services/ZipParser.php b/Services/ZipParser.php
new file mode 100644 (file)
index 0000000..822c0fd
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+
+namespace Services;
+
+use Exception;
+use ZipArchive;
+use Services\ISimfileParser;
+use Services\IBannerExtracter;
+use Domain\Entities\IFile;
+use Domain\Entities\StepMania\ISimfileStepByStepBuilder;
+use Domain\Entities\StepMania\IPackStepByStepBuilder;
+use Services\IZipParser;
+use Services\IUserSession;
+
+class ZipParser implements IZipParser
+{
+    private $_za;
+    private $_smFiles = array();
+    private $_smParser;
+    private $_smBuilder;
+    private $_packBuilder;
+    private $_bannerExtracter;
+    private $_userSession;
+    private $_file;
+    
+    public function __construct(
+        ISimfileParser $smParser,
+        ISimfileStepByStepBuilder $smBuilder,
+        IPackStepByStepBuilder $packBuilder,
+        IBannerExtracter $bannerExtracter,
+        IUserSession $userSession
+    ) {
+        $this->_smParser = $smParser;
+        $this->_smBuilder = $smBuilder;
+        $this->_packBuilder = $packBuilder;
+        $this->_bannerExtracter = $bannerExtracter;
+        $this->_userSession = $userSession;
+    }
+    
+    public function parse(IFile $file)
+    {
+        $this->_file = $file;
+        $this->_za = new ZipArchive();
+        //XXX: We assume all files are zips. Should be enforced by validation elsewhere.
+        $res = $this->_za->open('../files/StepMania/' . $file->getHash() . '.zip');
+
+        if($res !== true) throw new Exception ('Could not open zip for reading.');
+        $this->findSms();
+    }
+    
+    public function pack()
+    {
+        if(count($this->_smFiles) > 1)
+        {         
+            $packname = $this->packNameFromFiles();
+            $banner = $this->_bannerExtracter->extractPackBanner('../files/StepMania/' . $this->_file->getHash() . '.zip', $packname);
+            
+            /* @var $builder \Domain\Entities\StepMania\PackStepByStepBuilder */
+            $builder = $this->_packBuilder;
+            return $builder->With_Title($packname)
+                           ->With_Uploader($this->_userSession->getCurrentUser())
+                           ->With_Simfiles($this->_smFiles)
+                           ->With_Banner($banner)
+                           ->With_File($this->_file)
+                           ->build();
+        }
+    }
+        
+    public function simfiles()
+    {
+        return $this->_smFiles;
+    }
+    
+    public function isPack()
+    {
+        return count($this->_smFiles) > 1;
+    }
+    
+    public function isSingle()
+    {
+        return count($this->_smFiles) == 1;
+    }
+    
+    private function findSms()
+    {
+        for($i=0; $i<$this->_za->numFiles; $i++)
+        {
+            $stat = $this->_za->statIndex($i);
+            if(pathinfo($stat['name'], PATHINFO_EXTENSION) == 'sm')
+            {
+                $smData = file_get_contents('zip://../files/StepMania/' . $this->_file->getHash() . '.zip#' . $stat['name']);
+                $this->_smFiles[$stat['name']] = $this->SmDataToSmClass($smData);
+            }
+        }
+    }
+    
+    private function packNameFromFiles()
+    {
+        $packName = '';
+        $smpaths = array_keys($this->_smFiles);
+        foreach($smpaths as $path)
+        {
+            $pathComponents = explode('/', $path);
+            
+            if(empty($packName)) $packName = $pathComponents[0];
+            
+            if($packName != $pathComponents[0])
+                throw new Exception('Malformed zip. I found more than 1 sm file but the directory structure is not consistent with a pack.');
+        }
+        
+        return $packName;
+    }
+        
+    private function SmDataToSmClass($smData)
+    {
+        $parser = $this->_smParser;
+        $parser->parse($smData);
+
+        $banner = $this->_bannerExtracter->extractSongBanner('../files/StepMania/' . $this->_file->getHash() . '.zip', $parser->banner());
+
+        return $this->_smBuilder->With_Title($parser->title())
+                                ->With_Artist($parser->artist())
+                                ->With_Uploader($this->_userSession->getCurrentUser()) //obj
+                                ->With_BPM($parser->bpm())
+                                ->With_BpmChanges($parser->bpmChanges())
+                                ->With_Stops($parser->stops())
+                                ->With_FgChanges($parser->fgChanges())
+                                ->With_BgChanges($parser->bgChanges())
+                                ->With_Steps($parser->steps())
+                                ->With_Simfile($this->_file)
+                                ->With_Banner($banner)
+                                ->build();
+    }
+}
\ No newline at end of file
index 298fe53..574d4c1 100644 (file)
@@ -8,38 +8,45 @@ return [
     'facebook.app' => '../config/FacebookApp.php',\r
     \r
     //entites\r
-    'Domain\Entities\StepMania\ISimfile' => DI\object('Domain\Entities\StepMania\Simfile'),\r
-    'Domain\Entities\IUserStepByStepBuilder' => DI\object('Domain\Entities\UserStepByStepBuilder'),\r
-    'Domain\Entities\IUserBuilder' => DI\object('Domain\Entities\UserBuilder'),\r
-    'Domain\Entities\IUserFactory' => DI\object('Domain\Entities\UserFactory'),\r
+    'Domain\Entities\StepMania\ISimfileFactory'           => DI\object('Domain\Entities\StepMania\SimfileFactory'),\r
+    'Domain\Entities\StepMania\ISimfileBuilder'           => DI\object('Domain\Entities\StepMania\SimfileBuilder'),\r
     'Domain\Entities\StepMania\ISimfileStepByStepBuilder' => DI\object('Domain\Entities\StepMania\SimfileStepByStepBuilder'),\r
-    'Domain\Entities\StepMania\ISimfileBuilder' => DI\object('Domain\Entities\StepMania\SimfileBuilder'),\r
-    'Domain\Entities\StepMania\ISimfileFactory' => DI\object('Domain\Entities\StepMania\SimfileFactory'),\r
-    'Domain\Entities\IFileStepByStepBuilder' => DI\object('Domain\Entities\FileStepByStepBuilder'),\r
-    'Domain\Entities\IFileBuilder' => DI\object('Domain\Entities\FileBuilder'),\r
-    'Domain\Entities\IFileFactory' => DI\object('Domain\Entities\FileFactory'),\r
-    \r
+\r
+    'Domain\Entities\StepMania\IPackFactory'              => DI\object('Domain\Entities\StepMania\PackFactory'),\r
+    'Domain\Entities\StepMania\IPackBuilder'              => DI\object('Domain\Entities\StepMania\PackBuilder'),\r
+    'Domain\Entities\StepMania\IPackStepByStepBuilder'    => DI\object('Domain\Entities\StepMania\PackStepByStepBuilder'),\r
+\r
+    'Domain\Entities\IUserFactory'                        => DI\object('Domain\Entities\UserFactory'),\r
+    'Domain\Entities\IUserBuilder'                        => DI\object('Domain\Entities\UserBuilder'),\r
+    'Domain\Entities\IUserStepByStepBuilder'              => DI\object('Domain\Entities\UserStepByStepBuilder'),\r
+\r
+    'Domain\Entities\IFileFactory'                        => DI\object('Domain\Entities\FileFactory'),\r
+    'Domain\Entities\IFileBuilder'                        => DI\object('Domain\Entities\FileBuilder'),\r
+    'Domain\Entities\IFileStepByStepBuilder'              => DI\object('Domain\Entities\FileStepByStepBuilder'),\r
+\r
     //services\r
-    'Services\Http\IHttpResponse' => DI\object('Services\Http\HttpResponse'),\r
-    'Services\Http\IHttpRequest' => DI\object('Services\Http\HttpRequest'),\r
-    'Services\Routing\IRouter' => DI\object('Services\Routing\Router')\r
-        ->constructor(DI\link('router.maps')),\r
-    'Services\Uploads\IUploadManager' => DI\object('Services\Uploads\UploadManager'),\r
-    'Services\IUserSession' => DI\object('Services\UserSession'),\r
-    'Services\Uploads\IFileFactory' => DI\object('Services\Uploads\FileFactory'),\r
-    'Services\IFacebookSessionFactory' => DI\object('Services\FacebookSessionFactory')\r
-        ->constructor(DI\link('facebook.app')),\r
-    'Services\ISimfileParser' => DI\object('Services\SimfileParser'),\r
-    'Services\IBannerExtracter' => DI\object('Services\BannerExtracter'),\r
+    'Services\Http\IHttpResponse'                         => DI\object('Services\Http\HttpResponse'),\r
+    'Services\Http\IHttpRequest'                          => DI\object('Services\Http\HttpRequest'),\r
+    'Services\Routing\IRouter'                            => DI\object('Services\Routing\Router')\r
+                                                                ->constructor(DI\link('router.maps')),\r
+    'Services\Uploads\IUploadManager'                     => DI\object('Services\Uploads\UploadManager'),\r
+    'Services\IUserSession'                               => DI\object('Services\UserSession'),\r
+    'Services\Uploads\IFileFactory'                       => DI\object('Services\Uploads\FileFactory'),\r
+    'Services\IFacebookSessionFactory'                    => DI\object('Services\FacebookSessionFactory')\r
+                                                                ->constructor(DI\link('facebook.app')),\r
+    'Services\ISimfileParser'                             => DI\object('Services\SimfileParser'),\r
+    'Services\IZipParser'                                 => DI\object('Services\ZipParser'),\r
+    'Services\IBannerExtracter'                           => DI\object('Services\BannerExtracter'),\r
     \r
     //DA\r
-    'DataAccess\StepMania\ISimfileRepository' => DI\object('DataAccess\StepMania\SimfileRepository'),\r
-    'DataAccess\IUserRepository' => DI\object('DataAccess\UserRepository'),\r
-    'DataAccess\IFileRepository' => DI\object('DataAccess\FileRepository'),\r
-    'DataAccess\IDatabaseFactory' => DI\object('DataAccess\DatabaseFactory')\r
-        ->constructor(DI\link('db.credentials')),\r
-    'DataAccess\DataMapper\IDataMapper' => DI\object('DataAccess\DataMapper\DataMapper')\r
-        ->constructor(DI\link('datamapper.maps')),\r
-    'DataAccess\Queries\IQueryBuilderFactory' => DI\object('DataAccess\Queries\QueryBuilderFactory'),\r
+    'DataAccess\StepMania\ISimfileRepository'             => DI\object('DataAccess\StepMania\SimfileRepository'),\r
+    'DataAccess\StepMania\IPackRepository'                => DI\object('DataAccess\StepMania\PackRepository'),\r
+    'DataAccess\IUserRepository'                          => DI\object('DataAccess\UserRepository'),\r
+    'DataAccess\IFileRepository'                          => DI\object('DataAccess\FileRepository'),\r
+    'DataAccess\IDatabaseFactory'                         => DI\object('DataAccess\DatabaseFactory')\r
+                                                                ->constructor(DI\link('db.credentials')),\r
+    'DataAccess\DataMapper\IDataMapper'                   => DI\object('DataAccess\DataMapper\DataMapper')\r
+                                                                ->constructor(DI\link('datamapper.maps')),\r
+    'DataAccess\Queries\IQueryBuilderFactory'             => DI\object('DataAccess\Queries\QueryBuilderFactory'),\r
     \r
 ];\r
index fb9da91..1d6a2e7 100644 (file)
@@ -26,6 +26,18 @@ return [
         ]\r
     ],\r
     \r
+    'Pack' => [\r
+        'class' => 'Domain\Entities\StepMania\Pack',\r
+        'table' => 'packs',\r
+        'maps' => [\r
+            'title' => DataAccess\Varchar('title'),\r
+            'uploader' => DataAccess\Entity('User', 'getUploader'),\r
+            'simfiles' => DataAccess\EntityArray('Simfile', 'getSimfiles'),\r
+            'banner' => DataAccess\Entity('File', 'getBanner', 'banner_file'),\r
+            'file' => DataAccess\Entity('File', 'getFile')\r
+        ]\r
+    ],\r
+    \r
     'BPM' => [\r
         'class' => 'Domain\VOs\StepMania\BPM',\r
         'table' => 'simfiles',\r
index 1d0eda5..de8cf4b 100644 (file)
@@ -12,6 +12,12 @@ return [
         'controller' => 'Simfile',\r
         'action' => 'upload'\r
     ],\r
+    \r
+    //XXX: Test, delete later\r
+    '/simfiles/pack' => [\r
+        'methods' => ['GET'],\r
+        'controller' => 'PackTest',\r
+    ],\r
         \r
     '/simfiles/argTest/:testarg' => [\r
         'methods' => ['GET'],\r