phalcon / phalcon Goto Github PK
View Code? Open in Web Editor NEW[WIP] Phalcon Framework. Work will continue after release of v5.0
Home Page: https://phalcon.io
License: MIT License
[WIP] Phalcon Framework. Work will continue after release of v5.0
Home Page: https://phalcon.io
License: MIT License
Describe the bug
When using an expression with a placeholder after SET
in an UPDATE
query, I get the following error: Invalid parameter number: mixed named and positional parameters
. Probably because of the SELECT
query that is made before the UPDATE
query.
A pretty common scenario would be: SET col = col + some_number
.
To Reproduce
<?php
class Objects extends \Phalcon\Mvc\Model
{
public $obj_id;
public $obj_name;
public $obj_type;
public function initialize() {
$this->useDynamicUpdate(true);
// $this->skipAttributes(['obj_name']);
}
}
$di = new \Phalcon\Di\FactoryDefault;
$di->set('db', function() {
$db = new \Phalcon\Db\Adapter\Pdo\Mysql([
'dbname' => 'test',
'username' => 'test',
'password' => 'test',
]);
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach('db:afterQuery', function($ev, $db) {
echo $db->getSQLStatement() . "\n";
});
$db->setEventsManager($eventsManager);
return $db;
});
try {
/*\Phalcon\Mvc\Model::setup([
'events' => false,
'virtualForeignKeys' => false,
]);*/
$app = new \Phalcon\Mvc\Application($di);
$app->modelsManager->executeQuery(
'UPDATE Objects SET obj_type = obj_type + :obj_type: WHERE obj_id = 1', // err
// 'SET obj_type = obj_type + 2', // ok
// 'SET obj_type = :obj_type:', // ok
[
'obj_type' => 2
]
);
} catch ( Exception $ex ) {
print_r($ex->getMessage() . "\n");
}
Expected behavior
To be able to use placeholders in expressions after SET
.
Details
CREATE DATABASE IF NOT EXISTS `test`;
USE `test`;
CREATE TABLE IF NOT EXISTS `objects` (
`obj_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`obj_name` varchar(20) NOT NULL,
`obj_type` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`obj_id`)
) ENGINE=InnoDB;
INSERT INTO `objects` (`obj_id`, `obj_name`, `obj_type`) VALUES (1, 'test1', 1);
I have some raw-sql queries, which I want translate to PHQL. For example:
raw sql:
UPDATE versions
LEFT JOIN composerlink ON composerlink.TrackRef=versions.TrackRef
SET Price=PriceOrig
WHERE composerlink.ComposerRef=:artistId
AND (Price=0) OR (Price IS NULL)
And PHQL query of this sql query:
public static function updateVersions($artistId)
{
$artist = new Artist();
$phql = "
UPDATE Version
LEFT JOIN RelArtistTrack ON RelArtistTrack.TrackRef=Version.TrackRef
SET Version.Price=Version.PriceOrig
WHERE RelArtistTrack.ComposerRef=:artistId:
AND (Version.Price=0) OR (Version.Price IS NULL)
";
$result = $artist->getModelsManager()->executeQuery($phql, ['artistId' => $artistId]);
}
So, it's very simple UPDATE query with JOIN. But after executing this query I see an error:
Phalcon\Mvc\Model\Exception: Syntax error, unexpected token LEFT, near to ' JOIN RelArtistTrack ON RelArtistTrack.TrackRef=Version.TrackRef\n SET Version.Price=Version.PriceOrig\n WHERE RelArtistTrack.ComposerRef=:artistId:\n AND (Version.Price=0) OR (Version.Price IS NULL)\n ', when parsing: \n UPDATE Version\n LEFT JOIN RelArtistTrack ON RelArtistTrack.TrackRef=Version.TrackRef\n SET Version.Price=Version.PriceOrig\n WHERE RelArtistTrack.ComposerRef=:artistId:\n AND (Version.Price=0) OR (Version.Price IS NULL)\n (231)
If I understand correctly, these types of PHQL queries are not supported? Or this is a bug?
phalcon: 2.0.10
Reelated forum thread: https://forum.phalconphp.com/discussion/11065/update-query-with-join-in-phql
A noted in phalcon/cphalcon#13364 beanstalkd (may) be removed from phalcon in 4.0. I propose adding a redis queue adapter in place of beanstalkd. Otrher frameworks like Laravel have redis queue adapters and redis is well maintained (apart from on windows). For acheiving queues on redis RPOPLPUSH is recommended.
Proposed version: 4.0
In a normal html radio button, you can and normally use the same input name to select an option value:
i.e.:
<form>
<input type="radio" id="gender_male" name="gender" value="male"/>
<label for="gender_male">Male</label>
<input type="radio" id="gender_female" name="gender" value="female"/>
<label for="gender_female">Female</label>
</form>
But since Phalcon Forms use the component "name" and not "id" identify them you can't use them.
One way to add support would be use ids and not names to identify components in Phalcon\Form
Thanks!
Federico.
There should be a tag like the Smarty {literal} tag http://www.smarty.net/docsv2/en/language.function.literal
witch allows us to create a block of content that will be not parsed by the volt compiler and outputed as is!
This is useful if you want to put xml headers into a volt template like this:
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type='text/xsl' href='/s/sitemap.xsl'?>
without having the <?xml breaking the compiled template. Other use cases is if you really want to have in you html things like "{{ I relly want {{ }} in html }}"
This cold be achieved with something like
{% literal %}
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet type='text/xsl' href='/s/sitemap.xsl'?>
{% endliteral %}
{% literal %}
<b>{{</b> I relly want {{ }} in html <b>}}</b>
{% endliteral %}
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
Description
Since Volt is by default caching generated php templates, it might be useful to apply even time-consumpting filters on the output created at this stage. Filters could for example minify the HTML source code or do any further optimization.
Usage Example
use \Phalcon\View\Engine\Volt\Filters\HTMLMinify,
\Phalcon\Mvc\View\Engine\Volt;
[...]
$di->set('view', function() {
$view = new View();
$view->setViewsDir('../app/views/');
$view->registerEngines(array(
'.volt' => function($view, $di) {
$volt = new Volt($view, $di);
$volt->addFilter(new HTMLMinify());
return $volt;
}
));
return $view;
});
Framework Integration
The filter integration would match the filter integration of the Phalcon\Assets\Manager
class. All filters should implement FilterInterface
.
Suggested Additional Classes, Interfaces, Methods and Member Variables
\Phalcon\Mvc\View\Engine\Volt
class Volt extends Engine implements InjectionAwareInterface,
EventsAwareInterface, EngineInterface
{
/**
* Filters
*
* @var array|null
* @access protected
*/
protected $_filters = null;
/**
* Apply filter on output
*
* @param FilterInterface $filter
*/
public function addFilter(FilterInterface $filter) {}
/**
* Remove applied filter
*
* @param FilterInterface $filter
*/
public function removeFilter(FilterInterface $filter) {}
/**
* Get applied filters
*
* @return array
*/
public function getFilters() {}
}
\Phalcon\Mvc\View\Engine\Volt\FilterInterface
interface FilterInterface
{
/**
* Filters $content and returns the output
*
* @param string $content
* @return string
* @throws \Phalcon\Mvc\View\Exception
*/
public function filter($content);
}
\Phalcon\Mvc\View\Engine\Volt\Filters\HTMLMinify
class HTMLMinify implements FilterInterface
{
/**
* Applies php_strip_whitespace and minifies the HTML code around
*
* @param string $content
* @return string
* @throws \Phalcon\Mvc\View\Exception
*/
public function filter($content);
}
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
If would be nice if \Phalcon\Mvc\View\Engine\Volt could accept a Compiler in the constructor. This would be helpful when using the CLI interface to precompile every Volt view. As it currently stands it doesn't appear that I can use the Volt Compiler in a DI container and so I need to require() Volt configuration code in each place that I want to use the compiler.
Examples of what I need to currently do:
Web:
$compiler = $volt->getCompiler();
require($config->application->phalconDir . '/config/volt_compiler.php');
CLI:
$compiler = new Compiler();
require($config->application->phalconDir . '/config/volt_compiler.php');
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
This relates to phalcon/cphalcon#14675
The events manager stops execution if one event returns false
. This is true only if one listener is attached to the particular event.
The events manager needs to be updated to allow:
false
We can use stop()
to achieve this but a more configurable alternative would be nice.
I want to have a model which serves as some sort of base reference, and it has only one column: auto incremented PK.
Trying to save such model results in exception being thrown by Phalcon\Db\Adapter
line 319
Temp workaround: add nullable dummy field
Modern DI containers supprot autowire feature.
(http://php-di.org/doc/autowiring.html)
But phalcons not. And it could not be replaced by them.
It would be grate to make container autowireable.
Sometimes there are need to extend layout dynamically. Now it is possible to do only with relative path, ex: {% extends "layouts/base" %}
.
Related topic from forum - https://forum.phalcon.io/discussion/19331/volt-extends-from-virable
I would like to have possibility to set variable, relative path with ../
and absolute path inside {% extends ? %}
Examples:
{% extends "../another-theme/layouts/base" %}
{% extends $dynamicLayout %}
{% extends "/var/absolute/path/to/layout" %}
In Phalcon v4 we removed Beanstalkd (#13364, phalcon/cphalcon#13850) since the project is no longer supported.
This issue serves as a discussion and design board for potentially a new Queue adapter that can be used for future integrations with popular queue systems.
https://github.com/queue-interop/queue-interop is a good candidate
Alternatively, we can leave the queue system completely out of Phalcon and use implementations from the incubator or custom built ones from developers based on the needs of their applications.
Introduce the Data Mapper pattern for the ORM
https://martinfowler.com/eaaCatalog/dataMapper.html
The implementation is based/influenced by Atlas ORM
Related: phalcon/cphalcon#13022
Subtasks:
I don't know if we might gonna break the framework itself if we will include an additional key like this:
$di->get('router')->addGet('/tickets/{id}/edit', [
'controller' => '...',
'action' => '...',
'allowed' => [
'agent',
'marketing',
'moderator',
'reporter',
'administrator',
],
]);
We have the resource now in which helps a lot for acl, but the code above might be helpful too and might do a maintainable code I think...
we just need to call this function to add a role:
$di->get('acl')->allow('...')
then in the TicketController at editAction() we only need to call something like this
if ( $di->get('acl')->isAllowed() ) {}
Upon dispatching the router, it will analyze if the controller has limited roles, then it will check if the global acl assigned a role that matches on the controller, else it should return a false upon calling isAllowed() function.
FYI: The inputted keys and functions are just my suggestion, but hoping the same approach, it's up to you guys if you want to rename it.
Can we have a good Authenticaion library/adapther? A good example from Laravel [1] and yes I know about [2] Auth and [3] ACL in phalcon. What I'm talking about is more complete and easier to use library for beginners or those who don't want to code one themselfs. Because everyone coding auth/acl themselfs is unsecure.
You need to select full model like without columns
option. When you are selecting model with columns then always Phalcon\Mvc\Model\Row
is returned. It's not a bug.
Originally posted by @Jurigag in phalcon/cphalcon#12926 (comment)
Hello,
I've set up a multi-site, the code is all shared between both sites.
I Just change the db and caching port of the sites.
The problem is that the 2nd Application is getting keys from the 1st application.
Maybe phalcon is caching the config array in someplace we don't have access?
if (isset($_SERVER['Website2']))
$config = include __DIR__ . "/../app/config/config_2.php";
else
$config = include __DIR__ . "/../app/config/config.php";
this is the configuration file that differs each website.. It does work as planned because all data that is not cached comes without problems.
In services.php I set:
$di->setShared('modelsCache', function () use ($config) {
$frontCache = new \Phalcon\Cache\Frontend\Data(array("lifetime" => 600));
$cache = new Phalcon\Cache\Backend\Libmemcached($frontCache, array(
'servers' => array(
array('host' => $config->memcache->host,
'port' => $config->memcache->port,
'weight' => 1
),
),
'client' => array(Memcached::OPT_HASH => Memcached::HASH_MD5 )
));
return $cache;
});
When i try to get content from the Models in Website2, it return content from Website1. When i do a dump of the result, it shows me that it got from the other server (also i check in memcached and it does not have the key set).
Is there a hidden place where i should set this (and it's not in documentation so it would be a documentation bug)?
Is it a bug per se?
Thanks
Alan
3.2.0
)5.6.30
)For all components in the framework that implement the InjectionAware
interface this->get(service)
should be used, without any calls to Di::getDefault()
It should be the application's responsibility to set the DI container in the said component properly.
Also any calls to getShared
should be replaced with get()
because if the component is registered as shared get()
returns the shared instance
For now it's impossible to set attributes for drop-down list options using Phalcon\Tag::select()
I propose to implement it this way
$this->tag->select([
'robotId',
Robots::find(),
'using' => ['id', 'name'],
// parameter 'options' is array of attributes which must be set for OPTIONs
'options' => [
// class "favorite" will be set for OPTIONs with id 1 and 3
'class' => ['favorite', [1, 3]],
// "disabled" attribute will be set for destroyed robots
'disabled' => function($robot){return $robot->is_destroyed ? 'disabled' : false;},
// dir="rtl" will be set for all OPTIONs *
'dir' => 'rtl',
],
]);
* actually I don't know the reason to set the same attribute for every OPTION
Output:
<select name="robotId">
<option value="1" dir="rtl" class="favorite">Robocop</option>
<option value="2" dir="rtl" disabled="disabled">Unicron</option>
<option value="3" dir="rtl" class="favorite">Bender</option>
<option value="4" dir="rtl">R2-D2</option>
</select>
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
With the release of PHP 7.2, Argon2 has been available for use for password hashing under the PASSWORD_ARGON2I constant. The upcoming release of PHP 7.3 will build on this and enable PASSWORD_ARGON2D and PASSWORD_ARGON2ID.
Unlike BCRYPT, ARGON2 is resistant to GPU based attacks as it accesses the memory array in a password dependent order, which reduces the possibility of time–memory trade-off attack.
https://en.wikipedia.org/wiki/Argon2
https://password-hashing.net
https://github.com/P-H-C/phc-winner-argon2
http://php.net/manual/en/function.password-hash.php
Is your feature request related to a problem? Please describe.
https://docs.phalcon.io/4.0/en/di#load-from-config
Load services from config is pretty confusing in some cases, especially, when you want to wrap common approach service providers classes and just load them via $di->loadFromPhp()
.
Describe the solution you'd like
In my opition, current approach of loading the services, need to be review and rewritten or removed...
Example how would be nice to do, but you can't:
$di->loadFromPhp('services.php');
Currently binding entity to form with unmarked checkboxes won't set corresponding fields to anything. I use this code to fix this right now:
if ($this->request->isPost() && $form->isValid($_POST, $customer)) {
$customer->ours = isset($_POST['ours']) ? 1 : 0;
$customer->save();
}
Do I miss something?
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
The way many to many behaves, does not appear to be very intuitive, and is not nice to use. for example
$tag1 = \Tag::findFirst('id=1');
$tag2 = \Tag::findFirst('id=2');
$tag3 = \Tag::findFirst('id=3');
// create a post
$post = new \Post();
$post->tags = array($tag1, $tag2);
$post->save();
// edit a post
$post = \Post::findFirst('id=1'); // the same post that was created earlier
$post->tags = array($tag1, $tag3);
$post->save();
Will tend to mean the post ends up with all three tags, possibly even with an extra copy of tag1, rather than just 2 as might be expected, and how Doctrine works.
Is this a bug, or intended behaviour?
Some official word on this would be great, seems to attract a few people asking about it http://forum.phalconphp.com/discussion/2190/many-to-many-expected-behaviour
thanks :)
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
In Volt the string 'Let\'s Encrypt' should be handled like "Let's Encrypt".
However, using 'Let\'s Encrypt' causes a parser failure. This is especially problematic with strings that have both single and double quotes as there is no way to have them in one contained string.
This is suboptimal for translation efforts, where we need contained strings with longer text to be embedded into the templates in the first place.
Parse error: syntax error, unexpected 'to' (T_STRING), expecting ',' or ')'
It seems that the service returned using magic methods inside controllers are cached.
If I replace a default service inside a module, it won't be referenced properly inside controllers.
public function defaultAction($campaign, $pixel) {
$di = \Phalcon\DI\FactoryDefault::getDefault();
var_dump($di === $this->getDI(), $this->getDI()->get('request') === $this->request);
}
returns
bool(true)
bool(false)
This doesn't make sense: the internal DI is clearly the same as DI that is statically resolved, so I'm not sure where $this->request
is being retrieved from, but it isn't being taken from the DI at call time.
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
Phalcon 3.x
If the user tries to create a new behavior to duplicate records, the collection class can create unexpected errors,
class Robot extends Collection
{
public $name;
// This method will be triggered at each iteration on the records
public function __clone()
{
$this->_id = new MongoId;
}
...
}
# Part of https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/mvc/collection.zep
protected static function _getResultset(var params, <CollectionInterface> collection, connection, boolean unique)
{
...
if unique === true {
/**
* Requesting a single result
*/
documentsCursor->rewind();
let document = documentsCursor->current();
if typeof document != "array" {
return false;
}
/**
* Assign the values to the base object
*/
return static::cloneResult(base, document);
}
/**
* Requesting a complete resultset
*/
let collections = [];
for document in iterator_to_array(documentsCursor, false) {
/**
* Assign the values to the base object
*/
let collections[] = static::cloneResult(base, document);
}
return collections;
}
# Part of https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/mvc/collection.zep
public static function cloneResult(<CollectionInterface> collection, array! document) -> <CollectionInterface>
{
var clonedCollection, key, value;
let clonedCollection = clone collection;
for key, value in document {
clonedCollection->writeAttribute(key, value);
}
if method_exists(clonedCollection, "afterFetch") {
clonedCollection->{"afterFetch"}();
}
return clonedCollection;
}
abstract class Collection implements EntityInterface, CollectionInterface, InjectionAwareInterface, \Serializable
{
...
// Override
public final function __clone()
{
}
}
Hello again =))
It's a new feature request for 4.x.x again =))
The current behavior of the Regex and other validators that work with strings.:
When passing an array to the Regex validator, an array to string conversion error is thrown.
The new behavior of the Regex and other validators that work with strings:
When passing an array to the Regex validator must be returned the false value.
This should be achieved with an additional option to the validator. For example: 'failIfPassedNotString
UPD: Do not take PresenceOf in your head, wrote by accident.
Thnx.
CREATE TABLE `accommodation` (
...
`website` VARCHAR(60) DEFAULT NULL
...
// Request a transaction
$transaction = $this->transactionManager->get();
$accommodation = new Accommodation();
$accommodation->setTransaction($transaction);
...
$accommodationForm->bind(
$_POST,
$accommodation,
[
...
'website',
...
]
);
if ($accommodation->create() == false) {
$transaction->rollback(__('Error has occured while creating accommodation'), $accommodation);
}
...
// no errors, commit changes
$transaction->commit();
// website input
$website = new Text('website');
$website->setLabel(__('Website'));
/*$website->addValidator(
new Url(
[
"message" => __('Please enter valid website address')
]
)
);*/
$this->add($website);
DB field should be null
when nothing was entered in the field
DB field is empty string
When you generate a URL for a route for which the specified host, you can add the host in the URL.
$router->add('/login', array(
'module' => 'account',
'controller' => 'auth',
'action' => 'login'
))->setHostName('account.company.com')
->setName('account_login');
Now we get this URL
$url->get(array('for' => 'account_login')); // '/login'
But I would like to get the URL with the desired subdomain
$url->get(array('for' => 'account_login')); // 'account.company.com/login'
Hi,
I have a model-class in which I can set the source and database/schema dynamically. So I am able to switch the schema without having to create a separate definition for each model.
I get two different results - one from database 1 and one from database 2.
Phalcon searches within first database. After changing the schema, phalcon tries to get informations about database 2, but after that it still searches within database 1!
SQL
CREATE DATABASE `test_1`;
USE `test_1`;
CREATE TABLE `test_table` (
`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`text` VARCHAR(255) NOT NULL DEFAULT 'blah',
PRIMARY KEY (id));
INSERT INTO `test_1`.`test_table` (`id`) VALUES (1);
CREATE DATABASE `test_2`;
USE `test_2`;
CREATE TABLE `test_table` (
`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`text` VARCHAR(255) NOT NULL DEFAULT 'blubb',
PRIMARY KEY (id));
INSERT INTO `test_2`.`test_table` (`id`) VALUES (1);
PHP
namespace Mapserver\Models;
class TestModel extends \Phalcon\Mvc\Model
{
public static $database = '';
public static $source = 'test_table';
public function initialize()
{
$this->setConnectionService('db');
$this->setSchema(static::$database);
$this->setSource(static::$source);
}
public function getSource()
{
return static::$source;
}
public function getSchema()
{
return static::$database;
}
}
// I set the database/schema:
\Mapserver\Models\TestModel::$database = 'test_1';
// I try to find an object which is in database 1
$obj_1 = \Mapserver\Models\TestModel::findFirstById(1);
var_dump($obj_1->getSchema() );
var_dump($obj_1->toArray());
//Query is o.k, all works fine.
// I change the database/schema
\Mapserver\Models\TestModel::$database = 'test_2';
// I try to find an object which is in database 2
$obj_2 = \Mapserver\Models\TestModel::findFirstById(1);
var_dump($obj_2->getSchema() );
var_dump($obj_2->toArray());
Note, that $obj_2->getSchema() gives the correct database!
string(6) "test_1"
array(2) {
["id"]=>
string(1) "1"
["text"]=>
string(4) "blah"
}
string(6) "test_2"
array(2) {
["id"]=>
string(1) "1"
["text"]=>
string(4) "blah"
}
Take a look at the query-log:
[11:53:34] SELECT IF(COUNT(*) > 0, 1, 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`= 'test_table' AND `TABLE_SCHEMA` = 'test_1'
[11:53:34] DESCRIBE `test_1`.`test_table`
[11:53:34] SELECT `test_table`.`id`, `test_table`.`text` FROM `test_1`.`test_table` WHERE `test_table`.`id` = :0 [1]
[11:53:34] SELECT IF(COUNT(*) > 0, 1, 0) FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_NAME`= 'test_table' AND `TABLE_SCHEMA` = 'test_2'
[11:53:34] DESCRIBE `test_2`.`test_table`
[11:53:34] SELECT `test_table`.`id`, `test_table`.`text` FROM `test_1`.`test_table` WHERE `test_table`.`id` = :0 [1]
After changing the schema it queries: "DESCRIBE test_2
.test_table
"
but then it does: "SELECT ... FROM test_1
.test_table
WHERE ..."
Issue may be related to these old ones: phalcon/cphalcon#3018 and phalcon/cphalcon#2415
Something that I found in phalcon/cphalcon#14027 is that the Timestampable model behavior has the option to pass a string or an array of fields:
If you pass an array, those fields will be updated with the same timestamp and in the same format:
+-----+---------------------+---------------------+
| ID | Timestamp1 | Timestamp2 |
|-----|---------------------|---------------------|
| 123 | 2019-05-04 08:55:23 | 2019-05-04 08:55:23 |
+-----+---------------------+---------------------+
Why would anyone need to legitimately use this? Is it safe to remove passing as an array?
With inheritance of multiple files like a.volt extends b.volt extends c.volt, I have to use embedding blocks. Otherwise there's no profit to use inheritance more than one nesting level.
$current_url = 'http://example.com?page=1';
echo $url->get($current_url, ['page' => 5]);
My expectation must be:
http://example.com?page=5
But the returned url is:
http://example.com?page=1&page=5
Days ago I was working on a problem with the new instances returned by methods that call related elements in models in this publication
I have found a simple way to solve this problem by implementing a simple interface in the classes of models that we want to reuse globally in our applications
_reusable
protected property to static in Model Manager_reusable
property must remain as it isReusableInterface
namespace Phalcon\Mvc\Model;
interface ReusableInterface {
public function getUniqueKey();
}
if reusable {
if referencedModel instanceof ReusableInterface {
let uniqueKey = referencedModel->getUniqueKey();
} else {
let uniqueKey = unique_key(referencedModel, arguments);
}
let records = this->getReusableRecords(referencedModel, uniqueKey);
if typeof records == "array" || typeof records == "object" {
return records;
}
}
class Invoice extends \Phalcon\Mvc\Model implements \Phalcon\Mvc\Model\ReusableInterface {
const COL_ID = 'id';
const COL_DATE = 'date';
const REL_ITEMS = 'relItems';
private $id;
private $date;
public function initialize() {
$this->setSource('invoices');
$this->hasMany(
self::COL_ID,
Item::class,
Item::COL_INVOICE_ID,
[
'alias' => self::REL_ITEMS,
'reusable' => true,
]
);
}
public function getUniqueKey() {
return __CLASS__ . ':' . self::COL_ID . ':' . $this->getId();
}
public function getItems(): Simple
{
return $this->{self::REL_ITEMS};
}
public function setItems(array $items = []) : self
{
$this->{self::REL_ITEMS} = $items;
return $this;
}
// setters getters
}
class Item extends \Phalcon\Mvc\Model implements \Phalcon\Mvc\Model\ReusableInterface {
const COL_ID = 'id';
const COL_NAME = 'name';
const COL_PRICE = 'price';
const COL_INVOICE_ID = 'invoiceId';
const REL_INVOICE = 'relInvoice';
private $id;
private $name;
private $price;
private $invoiceId;
public function initialize() {
$this->setSource('invoices_items');
$this->belongsTo(
self::COL_INVOICE_ID,
Invoice::class,
Invoice::COL_ID,
[
'alias' => self::REL_INVOICE,
'foreignKey' => [
'allowNulls' => false,
'message' => 'Hey! where is my invoice?',
],
'reusable' => true,
]
);
}
public function getUniqueKey() {
return __CLASS__ . ':' . self::COL_ID . ':' . $this->getId();
}
public function getInvoice(): ?Invoice
{
return $this->{self::REL_INVOICE} ?: null;
}
public function setInvoice(Invoice $invoice) : self
{
$this->{self::REL_INVOICE} = $invoice;
return $this;
}
// setters getters
}
// After
$invoice = (new Invoice())->findFirst(1234);
var_dump(spl_object_hash($invoice)); // 00000000546e8820000000005fdc6415
var_dump(spl_object_hash($invoice->getItems()->getFirst()->getInvoice())); // 00000000546efff0000000005fdc6415
// Before
$invoice = (new Invoice())->findFirst(1234);
var_dump(spl_object_hash($invoice)); // 00000000546e8820000000005fdc6415
var_dump(spl_object_hash($invoice->getItems()->getFirst()->getInvoice())); // 00000000546e8820000000005fdc6415
Well I hope your comments on the matter. I hope I have been explicit enough in my idea
Rgds
->refresh()
uses ->assign()
We use beforeSave
, afterFetch
and afterSave
for property preparation, and we add setters with type hinting.
/**
* @param array $json_col
*
* @return Books
*/
public function setJsonCol(array $json_col): Books
{
$this->json_col = $json_col;
return $this;
}
Unless we set ' disableAssignSetters' => true
we get a type error.
Test tests/Integration/Behaviours/PropPreparationTraitTest.php:testRefresh
[TypeError] Argument 1 passed to CrazyFactory\Phalcon\Test\Models\Books::setJsonCol() must be of the type array, string given
phalcon/cphalcon#1 /var/www/project/tests/Models/Books.php:196
phalcon/cphalcon#2 CrazyFactory\Phalcon\Test\Models\Books->setJsonCol
phalcon/cphalcon#3 Phalcon\Mvc\Model->_possibleSetter
phalcon/cphalcon#4 Phalcon\Mvc\Model->assign
phalcon/cphalcon#5 /var/www/project/tests/Integration/Behaviours/PropPreparationTraitTest.php:63
phalcon/cphalcon#6 CrazyFactory\Phalcon\Test\Integration\Behaviours\PropPreparationTraitTest->testRefresh
Model::setup(['disableAssignSetters' => false]);
class Books extends Model
{
protected $jsonCol;
/**
* @param array $json_col
*
* @return Books
*/
public function setJsonCol(array $json_col): Books
{
$this->jsonCol = $json_col;
return $this;
}
/**
* @return array
*/
public function getJsonCol(): array
{
return $this->jsonCol;
}
public function afterFetch(): void
{
$this->jsonCol = json_decode($this->jsonCol, true);
}
public function afterSave(): void
{
...
public function beforeSave(): void
{
...
}
$model = Books::findFirst(1);
// do something
$model->refresh(); // problem time
Can we set properties in Phalcon\Mvc::refresh()
as we do in Phalcon\Mvc::cloneResult() instead of calling assign
? Then this->fireEvent("afterFetch"); will fire to prepare properties.
for example
"allowedTypes" => [
"image\/.*",
"video\/.*"
]
Well after some discussion we reverted my latest change to router to 3.2.x which is supposed to take advantage of router prefix to process routes faster. We decided to postpone this change to 4.0.0 with more changes to router. There is a list what i propose:
Phalcon\Mvc\Router\Group
, move necessary methods(like for example setPrefix
, setPaths
) to Phalcon\Mvc\Router
and add Phalcon\Mvc\Router::merge
so we can still have separated "route group" classes, just without duplicated/very similar codePhalcon\Mvc\Router::add
so it will accept Phalcon\Mvc\Router\RouteInterface
as first argument(keep old behavior too) or add separated method addRoute
Route::get
, Route::post
, etc and Route::via
to create route objectPhalcon\Mvc\Router\Collection
and actual logic for adding routes will happen there, Phalcon\Mvc\Router
will only call some methods$router->add("/products", "Products::get")->via(['POST', 'GET']);
The best will be just to throw some exception(just some flag in Route class which will be set after creating and adding it in Phalcon\Mvc\Router
) when user will try to do via on this point - because otherwise we will need to do a lot of dirty stuff here(like add it to all method groups and then remove it from not selected method groups etc). So user will need to change his code to:
$router->add("/products", "Products::get", ['POST', 'GET']);
or
$router->add(Route::via('/products', 'Products::get', ['POST', 'GET']));
If someone is using Phalcon\Mvc\Router\Group
the only change he will need to do is change extends Router\Group
to extends Router
and $router->mount
to $router->merge
.
My project has multiple modules for both mvc application & cli application.
There's many events attached in my project, like 'dispatch:beforeDispatchLoop'.
When this event triggered, from the "getModuleName" method of \Phalcon\Cli\Dispatcher can't get the module name, but \Phalcon\Mvc\Dispatcher did.
The source code of mvc application has called the "setModuleName" method when handle a request,
but the cli console hasn't.
So in cli mode, how to get the module name from the callback function of dispatch event?
Hi guys,
I think we need to implement a lock for the Redis session.
Without it may cause some unexpected bugs, like login failed,
because of the login request may take a little longer time to init some user data,
but the next request has arrived at the time.
There may have many ways to deal with this, using phpredis's session locking
phpredis session locking
or just add a new method to the session manager to give the chance for some check.
Exp:
public function hasRetry(string $key): bool
{
$has = false;
for ($i = 0; $i < 100; $i++)
{
$has = $this->has($key);
if ($has) { break; }
usleep(50000);
}
return $has;
}
In command line mode
The code is as follows :( fragment )
$arguments = [];
$arguments["task"] = 'Test';
$arguments["action"] = 'index';
$arguments["params"]=['a','b'];
$console->handle($arguments);
class MainTask extends Task
{
public function mainAction()
{
echo var_dump(func_get_args());
}
}
array(2) {
[0]=>
array(2) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
}
[1]=>
array(0) {
}
}
I read the source code and found "https://github.com/phalcon/cphalcon/blob/3.4.x/phalcon/dispatcher.zep#L886".
I read PHPDoc and my predicted output is:
array(2) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
}
It's different from what I think. Why?
I think hashes are more suitable for storing sessions, and the structure of the session is more similar to hashes
instead of string
set
or get
it.Hello.
During development, I noticed the strange behavior of phalcon when using Di inside Di.
If you use Di inside Di like this:
$di = new Di();
$di->setShared('app', function() {
$di = new Di();
/* some code... */
$app = new MvcApplication($di);
/* some code... */
return $app;
});
$di->get('app')->handle($_GET['_url'] ?? '/');
That in the returned instance Application container will contain the parent Di.
A study of the source showed this:
/**
* Pass the DI to the instance if it implements
* \Phalcon\Di\InjectionAwareInterface
*/
if typeof instance == "object" {
if instance instanceof InjectionAwareInterface {
instance->setDI(this);
}
}
The Di::get() method sets container even if it already exists.
Maybe we need to check if the container exists in the instance? See code below:
/**
* Pass the DI to the instance if it implements
* \Phalcon\Di\InjectionAwareInterface
*/
if typeof instance == "object" {
if instance instanceof InjectionAwareInterface && !(instance->getDi() instanceof InjectionAwareInterface) {
instance->setDI(this);
}
}
Hi
As you know as of Redis 3 , this system support clustering and sharding in a way that a system can have redundant session and cache backend .
I checked Zephir codes of Redis backend cache and noticed Phalcon calls it using
new Redis();
new version of phpredis (Redis php driver) now supports clustering and array (not sure when they've added Redis array support but Redis cluster support is somehow new )
but they can't be called using normal new Redis()
method and need to be called like these :
$obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"));
This way we will have HA redis cluster for backend cache and session (BTW the driver now supports session clustering and it can be done using php.ini , not tested by myself but i'll test it soon)
So it would be great if we can take advantage of Redis Cluster and Redist array natively in Phalcon .
Similar to issue phalcon/cphalcon#12166 it would be great if this could be taken a step further so that a custom Phalcon\Mvc\Model\Row
can be returned when using Query Builder for more complex queries.
At the moment, if you use Model::find()
methods and don't specify columns you are returned an instance of Model
. If you use joins and/or custom columns, the class of each row becomes Phalcon\Mvc\Model\Row
. If we could override this class, it would be possible to have re-usable utility methods to keep things DRY that would be usable in your View, Controller etc.
Looking at the source, it doesn't seem possible to override the use of Phalcon\Mvc\Model\Row
.
In your model class, similar to phalcon/cphalcon#12166:
namespace Models;
class Users extends \Phalcon\Mvc\Model {
public $id;
public $first_name;
public $last_name;
public function getResultsetRowClass() {
return 'Resultsets\Row\User';
}
}
namespace Resultsets\Row;
class User extends Phalcon\Mvc\Model\Row {
public function getFullName() {
return sprintf('%s %s', $this->first_name, $this->last_name);
}
}
You could even put the getFullName()
method in a trait so that the logic can be included in your model as well to keep things DRY.
Then in your view you could do something like:
{% for user in users %}
{{ user.id }}: {{ user.getFullName() }}
{% endfor %}
An alternative would be to add this when you're executing a built query:
$data = $queryBuilder->getQuery()->setResultsetRowClass('Resultsets\Row\User')->execute();
I'm by no means an expert but I think this might be more straight forward than adding to the model actually. We could have setResultsetRowClass
/ getResultsetRowClass
methods added into Phalcon\Mvc\Model\Query
which would be used on line 2816:
let resultObjectClass = this->getResultsetRowClass();
let resultObject = new {resultObjectClass}();
Right now we can set minResolution
and maxResolution
options.
But in responsive design is more useful have images with same aspect ratios.
For example if you show list of cards with images.
the code could be looks like that:
'aspectRatio' => [
'file' => '16by9' // or 16x9
]
I think it's a low priority issue.
but, we should know the possibility of an unintended result.
_exists() function always executes before create() or save() function in Model.
_exists() function => use read connection.
save() function => use write connection.
There may be long latency to replicate between master and slave servers.
We can't be guaranteed that slave servers always are same as master server.
It could be a bit problematic in high transaction.
In my opinion, you'd better use write connection intead of read connection in _exists() function.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.