/**
   @param $tId Int, TranslationId from the Page_Translations table.
   @return $ret [obj] || Exception
   obj will be arrays resembling JSON objects following this syntax:
   {
     Description: {
       Req: ''
     , Description: ''
     }
   , Match: ''
   , Original: ''
   , Translation: {
       TranslationId: 5
     , Translation: ''
     , Payload: ''
     , TranslationProvider: ''
     }
   }
   Returns all entries where obj.Original !== obj.Translation for the given $tid.
 */
 public function translationNotOriginal($tId)
 {
     if (!is_numeric($tId)) {
         //Sanity check on $tId
         return new Exception('$tId must be numeric!');
     }
     //Finding wanted cases:
     $ret = array();
     foreach ($this->descriptions as $tableName => $desc) {
         foreach ($desc['columns'] as $column) {
             //Fetching Description:
             $description = TranslationTableProjection::fetchDescription($column);
             //Fetching original entries:
             $columnName = $column['columnName'];
             $fieldSelect = $column['fieldSelect'];
             $q = "SELECT {$columnName} AS columnName, {$fieldSelect} AS fieldSelect " . "FROM {$tableName}";
             $originals = DataProvider::fetchAll($q);
             //[{columnName:…,fieldSelect:…}]
             //Searching changed translations:
             foreach ($originals as $row) {
                 //Potential entry for $ret:
                 $original = $row['columnName'];
                 $entry = array('Description' => $description, 'Original' => $original);
                 //$fieldSelect setup:
                 $fieldSelect = $row['fieldSelect'];
                 if ($desc['dependsOnStudy'] === true) {
                     $fieldSelect = implode('-', array($desc['study'], $fieldSelect));
                 }
                 //Fetching translation:
                 $category = $column['category'];
                 $q = "SELECT '{$fieldSelect}' AS payload, Trans FROM Page_DynamicTranslation " . "WHERE TranslationId = {$tId} " . "AND Category = '{$category}' " . "AND Field = '{$fieldSelect}' " . "AND Trans != '{$original}' " . "LIMIT 1";
                 foreach (DataProvider::fetchAll($q) as $tRow) {
                     //foreach acts as if
                     $entry['Translation'] = array('TranslationId' => $tId, 'Translation' => $tRow['Trans'], 'Payload' => $tRow['payload'], 'TranslationProvider' => $category);
                     array_push($ret, $entry);
                 }
             }
         }
     }
     return $ret;
 }
<?php

require_once 'categorySelection.php';
require_once 'query/translationTableProjection.php';
if (array_key_exists('tId', $_GET)) {
    $tId = intval($_GET['tId']);
} else {
    $tId = 1;
}
if (array_key_exists('tables', $_GET)) {
    $tables = explode(',', $_GET['tables']);
    $projection = TranslationTableProjection::projectTables($tables);
} else {
    $projection = TranslationTableProjection::projectAll();
}
if ($projection instanceof Exception) {
    Config::error($projection->getMessage(), true, true);
} else {
    $changed = $projection->translationNotOriginal($tId);
    require_once 'showTable.php';
    showTable(array('projection' => $changed));
}
?>
<script type="application/javascript">
<?php 
require_once 'js/translation.js';
?>
</script>
 /**
   @param $translationId
   @return $changed [[ Description => [Req => String, Description => String]
                    ,  Original => String
                    ,  Translation => [TranslationId => $translationId
                       , Translation => String, Payload => String, TranslationProvider => String]
                    ]]
   Returns an empty Array if $translationId === 1,
   because there cannot be changed translations in the source translation.
   Otherwise returns entries where translation 1 has a newer change than $translationId.
 */
 public static function getChangedTranslations($translationId)
 {
     $changed = array();
     if ($translationId !== 1) {
         $static = StaticTranslationProvider::getChanged($translationId);
         $dynamic = TranslationTableProjection::getChanged($translationId);
         $changed = array_merge($static, $dynamic);
     }
     return $changed;
 }
 /**
   @param $tId TranslationId the Translation to search
   @param $searchText String the Text to search
   @param $searchStrategy {'both','translation','original'}
   @return $ret [obj] || Exception
   obj will be arrays resembling JSON objects following this syntax:
   {
     Description: {Req: '', Description: ''}
   , Original: ''
   , Translation: {TranslationId: 5, Translation: '', Payload: '', TranslationProvider: ''}
   }
   Searches for the given $searchText and returns array to allow translation for found entries.
   $searchStrategy specifies if the originals, the translations or both should be searched.
 */
 public function search($tId, $searchText, $searchStrategy = 'both')
 {
     //Sanitizing $tId:
     $tId = is_numeric($tId) ? $tId : 1;
     //Sanitizing $searchText:
     $searchText = Config::getConnection()->escape_string($searchText);
     //Sanitizing $searchStrategy:
     if (preg_match('/^(both|translation|original)$/', $searchStrategy) === 0) {
         return new Exception("Invalid \$searchStrategy: '{$searchStrategy}'");
     }
     //Table to use:
     $tableName = $this->getTable();
     //Study to use:
     $study = $this->getStudy();
     // String || null
     //Column specific code:
     return $this->withColumn(function ($column) use($tId, $searchText, $searchStrategy, $tableName, $study) {
         $category = $column['category'];
         //Description to use for entries:
         $description = TranslationTableProjection::fetchDescription($column);
         //Payload -> $entry to prevent duplicates
         $payloadMap = array();
         //Searching in originals:
         if ($searchStrategy === 'both' || $searchStrategy === 'original') {
             $columnName = $column['columnName'];
             $fieldSelect = $column['fieldSelect'];
             $q = "SELECT {$columnName} AS columnName, {$fieldSelect} AS fieldSelect " . "FROM {$tableName} " . "WHERE {$columnName} LIKE '%{$searchText}%'";
             $originals = DataProvider::fetchAll($q);
             foreach ($originals as $original) {
                 $fieldSelect = $original['fieldSelect'];
                 if ($study !== null) {
                     $fieldSelect = "{$study}-{$fieldSelect}";
                 }
                 //Stub for $entry:
                 $entry = array('Description' => $description, 'Original' => $original['columnName'], 'Translation' => array('TranslationId' => $tId, 'Translation' => '', 'Payload' => $fieldSelect, 'TranslationProvider' => $category));
                 if ($study !== null) {
                     $entry['Study'] = $study;
                 }
                 //Trying to add existing translation:
                 $entry = $this->addTranslation($entry);
                 //Putting $entry into map:
                 $payloadMap[$fieldSelect] = $entry;
             }
         }
         //Searching in translations:
         if ($searchStrategy === 'both' || $searchStrategy === 'translation') {
             //Setting $columnName and $fieldSelect:
             $columnName = $column['columnName'];
             $fieldSelect = $column['fieldSelect'];
             //Need to fetch all originals to find matching translations:
             $q = "SELECT {$columnName} AS columnName, {$fieldSelect} AS fieldSelect " . "FROM {$tableName} ";
             $originals = DataProvider::fetchAll($q);
             foreach ($originals as $original) {
                 $fieldSelect = $original['fieldSelect'];
                 if ($study !== null) {
                     $fieldSelect = "{$study}-{$fieldSelect}";
                 }
                 //Preventing possible duplicates:
                 if (array_key_exists($fieldSelect, $payloadMap)) {
                     continue;
                 }
                 //Checking for translation:
                 $q = "SELECT Trans FROM Page_DynamicTranslation " . "WHERE TranslationId = {$tId} " . "AND Category = '{$category}' " . "AND Field = '{$fieldSelect}' " . "AND Trans LIKE '%{$searchText}%' " . "LIMIT 1";
                 foreach (DataProvider::fetchAll($q) as $r) {
                     //foreach works as if
                     $entry = array('Description' => $description, 'Original' => $original['columnName'], 'Translation' => array('TranslationId' => $tId, 'Translation' => $r['Trans'], 'Payload' => $fieldSelect, 'TranslationProvider' => $category));
                     if ($study !== null) {
                         $entry['Study'] = $study;
                     }
                     $payloadMap[$fieldSelect] = $entry;
                 }
             }
         }
         //Done:
         return array_values($payloadMap);
     });
 }