In a Phalcon project, I have multiple database in Oracle and mySQL with number of tables and views. Created models for corresponding tables and views. But unable to access views. I initialize in model below:
public function initialize()
{
$this->setConnectionService('dbBznes');
$this->setSchema('POLICY');
$this->setSource("BUSINESS_ALL");
}
As per the comments, this is apparently a metadata issue and the default metadata strategy is introspection and is attempting to check if the table exists. You can set up your own metadata strategy like so:
$di['modelsMetadata'] = function()
{
$metadata = new \Phalcon\Mvc\Model\MetaData\Memory();
$metadata->setStrategy(
new MyIntrospectionStrategy()
);
return $metadata;
};
"Memory" in this case means don't use any sort of metadata caching. This goes off into another tangent as you can cache in many ways for more speed in production, etc.
As for the MyIntrospectionStrategy class above, it represents your own class based on Phalcon's Introspection strategy which attempts to analyze the database to figure out the fields and their types involved with the table.
I believe I converted Phalcon\Mvc\Model\MetaData\Strategy\Introspection from Zephir to PHP correctly as follows:
class MyIntrospectionStrategy implements \Phalcon\Mvc\Model\MetaData\StrategyInterface
{
public final function getMetaData(\Phalcon\Mvc\ModelInterface $model, \Phalcon\DiInterface $dependencyInjector)
{
$schema = $model->getSchema();
$table = $model->getSource();
$readConnection = $model->getReadConnection();
if( !$readConnection->tableExists($table, $schema) )
{
if($schema)
{
$completeTable = $schema . "'.'" . $table;
} else {
$completeTable = $table;
}
throw new \Phalcon\Mvc\Model\Exception(
"Table '" . $completeTable . "' doesn't exist in database when dumping meta-data for " . get_class($model)
);
}
$columns = $readConnection->describeColumns($table, $schema);
if( !count($columns) )
{
if($schema)
{
$completeTable = $schema . "'.'" . $table;
} else {
$completeTable = $table;
}
/**
* The table not exists
*/
throw new \Phalcon\Mvc\Model\Exception(
"Cannot obtain table columns for the mapped source '" . completeTable . "' used in model " . get_class(model)
);
}
$attributes = [];
$primaryKeys = [];
$nonPrimaryKeys = [];
$numericTyped = [];
$notNull = [];
$fieldTypes = [];
$fieldBindTypes = [];
$automaticDefault = [];
$identityField = false;
$defaultValues = [];
$emptyStringValues = [];
foreach($columns as $column)
{
$fieldName = $column->getName();
$attributes[] = $fieldName;
if ($column->isPrimary() === true)
{
$primaryKeys[] = $fieldName;
} else {
$nonPrimaryKeys[] = $fieldName;
}
if ($column->isNumeric() === true)
{
$numericTyped[$fieldName] = true;
}
if ($column->isNotNull() === true)
{
$notNull[] = $fieldName;
}
if ($column->isAutoIncrement() === true)
{
$identityField = $fieldName;
}
$fieldTypes[$fieldName] = $column->getType();
$fieldBindTypes[$fieldName] = $column->getBindType();
$defaultValue = $column->getDefault();
if ($defaultValue !== null || $column->isNotNull() === false)
{
if ( !$column->isAutoIncrement() )
{
$defaultValues[$fieldName] = $defaultValue;
}
}
}
return [
\Phalcon\Mvc\Model\MetaData::MODELS_ATTRIBUTES => $attributes,
\Phalcon\Mvc\Model\MetaData::MODELS_PRIMARY_KEY => $primaryKeys,
\Phalcon\Mvc\Model\MetaData::MODELS_NON_PRIMARY_KEY => $nonPrimaryKeys,
\Phalcon\Mvc\Model\MetaData::MODELS_NOT_NULL => $notNull,
\Phalcon\Mvc\Model\MetaData::MODELS_DATA_TYPES => $fieldTypes,
\Phalcon\Mvc\Model\MetaData::MODELS_DATA_TYPES_NUMERIC => $numericTyped,
\Phalcon\Mvc\Model\MetaData::MODELS_IDENTITY_COLUMN => $identityField,
\Phalcon\Mvc\Model\MetaData::MODELS_DATA_TYPES_BIND => $fieldBindTypes,
\Phalcon\Mvc\Model\MetaData::MODELS_AUTOMATIC_DEFAULT_INSERT => $automaticDefault,
\Phalcon\Mvc\Model\MetaData::MODELS_AUTOMATIC_DEFAULT_UPDATE => $automaticDefault,
\Phalcon\Mvc\Model\MetaData::MODELS_DEFAULT_VALUES => $defaultValues,
\Phalcon\Mvc\Model\MetaData::MODELS_EMPTY_STRING_VALUES => $emptyStringValues
];
}
public final function getColumnMaps(\Phalcon\Mvc\ModelInterface $model, \Phalcon\DiInterface $dependencyInjector)
{
$orderedColumnMap = null;
$reversedColumnMap = null;
if (method_exists($model, 'columnMap'))
{
$userColumnMap = $model->columnMap();
if ( gettype($userColumnMap) != 'array')
{
// Bad grammer directly in cphalcon :sadface:
throw new \Phalcon\Mvc\Model\Exception('columnMap() not returned an array');
}
$reversedColumnMap = [];
$orderedColumnMap = $userColumnMap;
foreach($userColumnMap as $name => $userName)
{
$reversedColumnMap[$userName] = $name;
}
}
return [$orderedColumnMap, $reversedColumnMap];
}
}
I have not tested this.
As far as adding support for views to be treated like tables, the change might be as simple as:
Before:
if( !$readConnection->tableExists($table, $schema) )
After:
if( !$readConnection->tableExists($table, $schema) && !$readConnection->viewExists($table, $schema) )
If this doesn't work due to logic choking with describeColumns, you might need to write something specific for working with views in Oracle for this dialect.
As far as other solutions, you can provide your own metadata method directly on the model by specifying your ownmetaData method directly on it.
Another solution is to use annotations instead of introspection for metadata.
Then you'd place your metadata as comments in the code for Phalcon to parse.
If you continue to run into problems with Database Views, just run it as raw SQL rather than attempting to use the ORM to do it. You can simply define a new method on your model to run the raw SQL.
Related
I am getting the following error while trying to migrate. Actually, I have a lot of data so I used the chunk in laravel.
I am using MongoDB not SQL
** You must specify an orderBy clause when using this function.**
My migration file's up function is is
public function up()
{
ini_set('max_execution_time', 250);
ini_set('memory_limit', '1024M');
$product_array = DB::collection('product')->orderBy('created_at','asc');
$product_array->chunk(20, function($product_array)
{
$cotationPercentForecast=0;
$cotationPercent=0;
$quote_tpl = array();
foreach ($product_array as $key => $value) {
if (isset($value['quote_tpl'])) {
$quote_tpl = $value['quote_tpl'];
$defaultScoreCount = 0;
$realScoreCount = 0;
$forecastScoreCount = 0;
foreach ($quote_tpl as $idxPerimetre => $perimetre) {
if (isset($perimetre['families'])) {
foreach ($perimetre['families'] as $idxFamilies => $family) {
if (isset($family['subfamilies'])) {
foreach ($family['subfamilies'] as $idxSubFamilies => $subfamily) {
if (isset($subfamily['items'])) {
foreach ($subfamily['items'] as $idxitem => $item) {
if (isset($item['criterias'])) {
foreach ($item['criterias'] as $idxcriteria => $criteria) {
if (!isset($criteria['inactive']) || $criteria['inactive'] != 'true') {
$defaultScoreCount++;
if (isset($criteria['real_score'])) {
$realScoreCount++;
}
if (isset($criteria['forecast_score'])) {
$forecastScoreCount++;
}
}
} // FOREACH CRITERIA
} // ISSET CRITERIA ###
} // FOREACH ITEM
} // ISSET ITEM ###
} // FOREACH SUBFAMILY
} // ISSET SUBFAMILY ###
} // FOREACH FAMILY
} // ISSET FAMILY ###
}// FOREACH PERIMETER ##
if ($realScoreCount > 0 && $defaultScoreCount > 0) {
$cotationPercentUn = ($realScoreCount / $defaultScoreCount) * 100;
$cotationPercent = round($cotationPercentUn, 2);
}
if ($forecastScoreCount > 0 && $defaultScoreCount > 0) {
$cotationPercentUnForecast = ($forecastScoreCount / $defaultScoreCount) * 100;
$cotationPercentForecast = round($cotationPercentUnForecast, 2);
}
$results = DB::collection('product')->update(['cotation_percent' => $cotationPercent,'forecast_cotation_percent' => $cotationPercentForecast]);
}
}
});
}
I have gone through many answers here and almost all suggest to use orderBy so I used it. But still getting the error.
i have used
Product::chunk(5, function($product_array){}
Any help is appreciated. Thanks in advance.
This warning is added when Laravel internally uses enforceOrderBy which is defined inside Illuminate/Database/Query/Builder.
Whenever you use chunk on a query builder instantiated by DB facade Illuminate\Database\Query\Builder directly, it would ask you :
You must specify an orderBy clause when using this function.
So it will happen if you are doing :
\DB::table('product')->chunk(10, function($product){
...
});
If you manually add an order by to this, it will not throw the error and will work as expected :
\DB::table('product')->orderBy('created_at')->chunk(10, function($product){
...
});
However, its better to use the Eloquent Model directly like which will not enforce you to add an order by clause manually :
Product::chunk(10, function($product){
...
});
Also there is no method DB::collection(), you can use DB::table() instead if you wish, unless you are using mongodb and not MySQL
I have used more time with Laravel 5.0 on database transaction but when I change to Laravel 5.3.* it not work as I want even I have disabled commit() method all data still continue insert into database.
if ($request->isMethod('post')) {
DB::beginTransaction();
$cats = new Cat();
$cats->parent_id = $this->request->input('category_id');
$cats->status = ($this->request->input('status')) ? $this->request->input('status') : 0;
if ($res['cat'] = $cats->save()) {
$catD = new CategoryDescriptions();
$catD->category_id = $cats->id;
$catD->language_id = 1;
$catD->name = $this->request->input('en_name');
if ($res['cat2'] = $catD->save()) {
$catD2 = new CategoryDescriptions();
$catD2->category_id = $cats->id;
$catD2->language_id = 2;
$catD2->name = $this->request->input('kh_name');
$res['all'] = $catD2->save();
}
}
if ($res) {
//DB::commit();
return $res;
}
return [false];
}
Check this line ($res['cat'] is a boolean):
if($res['cat'] = $cats->save()) {
And this ($res is an array. You compare an array as boolean!):
if($res){
The right condition should be:
$res = array(); // the first line of your method
// .... your http method check, start transaction, etc.
if (!in_array(false, $res, true)) { // check if array doesn't contain 'false values'
DB::commit();
}
I use codeigniter 3.0.6 query string like
index.php?d=directoryt&c=controller
index.php?d=directory&c=controller&m=function
How ever having two get methods for directory and controller is a bit long.
Question Is there any way to modify the protected function
_set_routing() function using a MY_Router.php to get it so it will pick up the directory and controller by using one query only like example below.
index.php?route=directory/controller
// If need to get function
index.php?route=directory/controller&m=function
What have tried so far
<?php
class MY_Router extends CI_Router {
protected function _set_routing()
{
// Load the routes.php file. It would be great if we could
// skip this for enable_query_strings = TRUE, but then
// default_controller would be empty ...
if (file_exists(APPPATH.'config/routes.php'))
{
include(APPPATH.'config/routes.php');
}
if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
{
include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
}
// Validate & get reserved routes
if (isset($route) && is_array($route))
{
isset($route['default_controller']) && $this->default_controller = $route['default_controller'];
isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes'];
unset($route['default_controller'], $route['translate_uri_dashes']);
$this->routes = $route;
}
// Are query strings enabled in the config file? Normally CI doesn't utilize query strings
// since URI segments are more search-engine friendly, but they can optionally be used.
// If this feature is enabled, we will gather the directory/class/method a little differently
if ($this->enable_query_strings)
{
// If the directory is set at this time, it means an override exists, so skip the checks
if ( ! isset($this->directory))
{
$_route = isset($_GET['route']) ? trim($_GET['route'], " \t\n\r\0\x0B/") : '';
if ($_route !== '')
{
echo $_route;
$this->uri->filter_uri($_route);
$this->set_directory($_route);
}
}
// Routing rules don't apply to query strings and we don't need to detect
// directories, so we're done here
return;
}
// Is there anything to parse?
if ($this->uri->uri_string !== '')
{
$this->_parse_routes();
}
else
{
$this->_set_default_controller();
}
}
}
config.php
$config['allow_get_array'] = TRUE;
$config['enable_query_strings'] = TRUE;
$config['controller_trigger'] = 'c';
$config['function_trigger'] = 'm';
$config['directory_trigger'] = 'd';
// Modifyed in MY_Router.php
$config['route'] = 'route';
I have it working
<?php
class MY_Router extends CI_Router {
protected function _set_routing() {
if (file_exists(APPPATH.'config/routes.php'))
{
include(APPPATH.'config/routes.php');
}
if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
{
include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
}
// Validate & get reserved routes
if (isset($route) && is_array($route))
{
isset($route['default_controller']) && $this->default_controller = $route['default_controller'];
isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes'];
unset($route['default_controller'], $route['translate_uri_dashes']);
$this->routes = $route;
}
if ($this->enable_query_strings) {
if ( ! isset($this->directory))
{
$route = isset($_GET['route']) ? trim($_GET['route'], " \t\n\r\0\x0B/") : '';
if ($route !== '')
{
$part = explode('/', $route);
$this->uri->filter_uri($part[0]);
$this->set_directory($part[0]);
if ( ! empty($part[1])) {
$this->uri->filter_uri($part[1]);
$this->set_class($part[1]);
// Testing function atm
if ( ! empty($_GET['function']))
{
$this->uri->filter_uri($_GET['function']);
$this->set_method($_GET['function']);
}
$this->uri->rsegments = array(
1 => $this->class,
2 => $this->method
);
}
} else {
$this->_set_default_controller();
}
}
// Routing rules don't apply to query strings and we don't need to detect
// directories, so we're done here
return;
}
// Is there anything to parse?
if ($this->uri->uri_string !== '')
{
$this->_parse_routes();
}
else
{
$this->_set_default_controller();
}
}
}
i am tyring to add categories programmatically for german and english languages.
website-ID: 1
website store-ID: 1
view-ID english, code "en" => 1
view-ID german, code "de" => 2
$category['general']['path'] = $v['cat_name_de'];
$category['general']['name'] = $v['cat_name_de'];
$category['general']['meta_title'] = "";
$category['general']['meta_description'] = "";
$category['general']['is_active'] = 1;
$category['general']['url_key'] = urlencode($v['cat_name_de']);
$category['general']['display_mode'] = "PRODUCTS";
$category['general']['is_anchor'] = 0;
$category['category']['parent'] = $v['magento_cat_id'];
$storeId = 0; // default
$magento_subcat_id = createCategory($category, $storeId);
...
function createCategory($data, $storeId) {
echo "Erzeuge '{$data['general']['name']}' - Elternkategorie:
[{$data['category']['parent']}] ...";
$category = Mage::getModel('catalog/category');
$category->setStoreId($storeId);
if (is_array($data)) {
$category->addData($data['general']);
if (!$category->getId()) {
$parentId = $data['category']['parent'];
if (!$parentId) {
if ($storeId) {
$parentId = Mage::app()->getStore($storeId)->getRootCategoryId();
} else {
$parentId = Mage_Catalog_Model_Category::TREE_ROOT_ID;
}
}
$parentCategory = Mage::getModel('catalog/category')->load($parentId);
$category->setPath($parentCategory->getPath());
}
if ($useDefaults = $data['use_default']) {
foreach ($useDefaults as $attributeCode) {
$category->setData($attributeCode, null);
}
}
$category->setAttributeSetId($category->getDefaultAttributeSetId());
if (isset($data['category_products']) &&
!$category->getProductsReadonly()) {
$products = array();
parse_str($data['category_products'], $products);
$category->setPostedProducts($products);
}
try {
$category->save();
return $category->getId();
} catch (Exception $e) {
return FALSE;
}
}
}
What i am puzzling with at the moment is setting view-ID individually for passing the $category['general']['name'] for each language.
I tried to set each language string with a foreach setting english/german texts. But this did not work out, because i somehow mixed up setting the view correctly.
To make it short:
Creating categories and subcategories like this does work very good, but i do not get it to work to pass english/german categoty titles.
Thanks guys
For all of you who are struggeling with this situation i made a workaound like this:
I added all those values to the affected database tables directly. This is for sure a quick and dirty way, but it does the job.
For all in need of some ideas here is the (undocumented) snipped belonging to my initial post.
// Get values for next language insert
$entity_type_id = $category
->getResource()
->getTypeId();
$entity_id = $category->getEntityId();
$read_entity = Mage::getSingleton('core/resource')
->getConnection('core_write');
$sql_entity = "SELECT * FROM eav_attribute WHERE
`attribute_code` = 'name' AND
`entity_type_id` = " . $entity_type_id . ";";
$row_entity = $read_entity
->fetchRow($sql_entity);
$attribute_id = $row_entity['attribute_id'];
// prepare value for name field in different langauge
$val = Mage::getSingleton('core/resource')
->getConnection('default_write')
->quote($v['en']);
$sql_insert = "INSERT INTO `catalog_category_entity_varchar`
(`entity_type_id`,`attribute_id`,`store_id`,`entity_id`,`value`)
VALUES ($entity_type_id,$attribute_id,$storeId,$entity_id,$val);";
$read_entity->query($sql_insert);
I hope this helps someone out who is in need of this.
I am trying to create several thumbs of different sizes using a foreach loop on a resize method.
$sizes = array(
'thumb' => Configure::read('Shop.image_thumb_dimensions'),
'medium' => Configure::read('Shop.image_medium_dimensions'),
'large' => Configure::read('Shop.image_large_dimensions')
);
foreach($sizes as $folder => $size) {
$destFolder = WWW_ROOT. $this->upload_dir . DS . $folder;
if (!file_exists($destFolder)) {
#mkdir($destFolder);
}
$dimensionsArray = explode(',', $size);
$newWidth = $dimensionsArray[0];
$newHeight = $dimensionsArray[1];
$destFile = $destFolder . DS . $fileName;
$resize = $this->__resize($filePath, $destFile, $newWidth, $newHeight);
}
and then the resize function which uses some methods from a component goes like this:
private function __resize($src, $destFile, $newWidth, $newHeight) {
$this->Watimage->setImage($src);
$this->Watimage->resize(array('type' => 'resizecrop', 'size' => array($newWidth, $newHeight)));
if ( !$this->Watimage->generate($destFile) ) {
// handle errors...
return $this->Watimage->errors;
}
else {
return true;
}
}
So this works great for the first image size (the thumb) but thereafter I get the error:
b>Notice</b> (8)</a>: Indirect modification of overloaded property WatimageComponent::$file has no effect [<b>APP/Plugin/Gallery/Controller/Component/WatimageComponent.php</b>, line <b>114</b>
I don't understand what I am doing wrong?? Have spent hours trying to figure this out.
Any illumination on the matter will be greatly appreciated.
This is the method from the component class:
public function setImage($file) {
// Remove possible errors...
$this->errors = array();
try
{
if ( is_array($file) && isset($file['file']) )
{
if ( isset($file['quality']) )
$this->setQuality($file['quality']);
$file = $file['file'];
}
elseif ( empty($file) || (is_array($file) && !isset($file['file'])) )
{
throw new Exception('Empty file');
}
if ( file_exists($file) )
$this->file['image'] = $file;
else
throw new Exception('File "' . $file . '" does not exist');
// Obtain extension
$this->extension['image'] = $this->getFileExtension($this->file['image']);
// Obtain file sizes
$this->getSizes();
// Create image boundary
$this->image = $this->createImage($this->file['image']);
$this->handleTransparentImage();
}
catch ( Exception $e )
{
$this->error($e);
return false;
}
return true;
}
There you go, the initial problem is most probably the unsetting of the WaitmageComponent::$file property
unset($this->file);
https://github.com/elboletaire/Watimage/blob/b72e7ac17ad30bfc47ae4d0f31c4ad6795c8f8d2/watimage.php#L706
After doing so, the magic property accessor Component::__get() will kick in when trying to access the now unexistent WaitmageComponent::$file property, and consequently this results in the warning you are receiving.
Instead of unsetting the variable, it should be reinitialized:
$this->file = array();
And of course it should also be initialized properly:
private $file = array();
You should initialize the property on your class, i think what is going on is you are trying to do something like:
$this->file = $var;
But you need to tell your class what is the $file property is:
class WaitmageComponent extends Component {
public $file = array();
}