I am working with SilverStripe, and I am working on making a newspage.
I use the DataObjectAsPage Module( http://www.ssbits.com/tutorials/2012/dataobject-as-pages-the-module/ ), I got it working when I use the admin to publish newsitems.
Now I want to use the DataObjectManager Module instead of the admin module to manage my news items. But this is where the problem exists. Everything works fine in draft mode, I can make a new newsitem and it shows up in draft. But when I want to publish a newsitem, it won't show up in the live or published mode.
I'm using the following tables:
-Dataobjectaspage table,
-Dataobjectaspage_live table,
-NewsArticle table,
-NewsArticle_Live table
The Articles have been inserted while publishing in the Dataobjectaspage table and in the NewsArticle table... But not in the _Live tables...
Seems the doPublish() function hasn't been used while 'Publishing'.
So I'm trying the use the following:
function onAfterWrite() {
parent::onAfterWrite();
DataObjectAsPage::doPublish();
}
But when I use this, it gets an error:
here is this picture
It seems to be in a loop....
I've got the NewsArticle.php file where I use this function:
function onAfterWrite() {
parent::onAfterWrite();
DataObjectAsPage::doPublish();
}
This function calls the DataObjectAsPage.php file and uses this code:
function doPublish() {
if (!$this->canPublish()) return false;
$original = Versioned::get_one_by_stage("DataObjectAsPage", "Live", "\"DataObjectAsPage\".\"ID\" = $this->ID");
if(!$original) $original = new DataObjectAsPage();
// Handle activities undertaken by decorators
$this->invokeWithExtensions('onBeforePublish', $original);
$this->Status = "Published";
//$this->PublishedByID = Member::currentUser()->ID;
$this->write();
$this->publish("Stage", "Live");
// Handle activities undertaken by decorators
$this->invokeWithExtensions('onAfterPublish', $original);
return true;
}
And then it goes to DataObject.php file and uses the write function ():
public function write($showDebug = false, $forceInsert = false, $forceWrite = false, $writeComponents = false) {
$firstWrite = false;
$this->brokenOnWrite = true;
$isNewRecord = false;
if(self::get_validation_enabled()) {
$valid = $this->validate();
if(!$valid->valid()) {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
throw new ValidationException($valid, "Validation error writing a $this->class object: " . $valid->message() . ". Object not written.", E_USER_WARNING);
return false;
}
}
$this->onBeforeWrite();
if($this->brokenOnWrite) {
user_error("$this->class has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR);
}
// New record = everything has changed
if(($this->ID && is_numeric($this->ID)) && !$forceInsert) {
$dbCommand = 'update';
// Update the changed array with references to changed obj-fields
foreach($this->record as $k => $v) {
if(is_object($v) && method_exists($v, 'isChanged') && $v->isChanged()) {
$this->changed[$k] = true;
}
}
} else{
$dbCommand = 'insert';
$this->changed = array();
foreach($this->record as $k => $v) {
$this->changed[$k] = 2;
}
$firstWrite = true;
}
// No changes made
if($this->changed) {
foreach($this->getClassAncestry() as $ancestor) {
if(self::has_own_table($ancestor))
$ancestry[] = $ancestor;
}
// Look for some changes to make
if(!$forceInsert) unset($this->changed['ID']);
$hasChanges = false;
foreach($this->changed as $fieldName => $changed) {
if($changed) {
$hasChanges = true;
break;
}
}
if($hasChanges || $forceWrite || !$this->record['ID']) {
// New records have their insert into the base data table done first, so that they can pass the
// generated primary key on to the rest of the manipulation
$baseTable = $ancestry[0];
if((!isset($this->record['ID']) || !$this->record['ID']) && isset($ancestry[0])) {
DB::query("INSERT INTO \"{$baseTable}\" (\"Created\") VALUES (" . DB::getConn()->now() . ")");
$this->record['ID'] = DB::getGeneratedID($baseTable);
$this->changed['ID'] = 2;
$isNewRecord = true;
}
// Divvy up field saving into a number of database manipulations
$manipulation = array();
if(isset($ancestry) && is_array($ancestry)) {
foreach($ancestry as $idx => $class) {
$classSingleton = singleton($class);
foreach($this->record as $fieldName => $fieldValue) {
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
$fieldObj = $this->dbObject($fieldName);
if(!isset($manipulation[$class])) $manipulation[$class] = array();
// if database column doesn't correlate to a DBField instance...
if(!$fieldObj) {
$fieldObj = DBField::create('Varchar', $this->record[$fieldName], $fieldName);
}
// Both CompositeDBFields and regular fields need to be repopulated
$fieldObj->setValue($this->record[$fieldName], $this->record);
if($class != $baseTable || $fieldName!='ID')
$fieldObj->writeToManipulation($manipulation[$class]);
}
}
// Add the class name to the base object
if($idx == 0) {
$manipulation[$class]['fields']["LastEdited"] = "'".SS_Datetime::now()->Rfc2822()."'";
if($dbCommand == 'insert') {
$manipulation[$class]['fields']["Created"] = "'".SS_Datetime::now()->Rfc2822()."'";
//echo "<li>$this->class - " .get_class($this);
$manipulation[$class]['fields']["ClassName"] = "'$this->class'";
}
}
// In cases where there are no fields, this 'stub' will get picked up on
if(self::has_own_table($class)) {
$manipulation[$class]['command'] = $dbCommand;
$manipulation[$class]['id'] = $this->record['ID'];
} else {
unset($manipulation[$class]);
}
}
}
$this->extend('augmentWrite', $manipulation);
// New records have their insert into the base data table done first, so that they can pass the
// generated ID on to the rest of the manipulation
if(isset($isNewRecord) && $isNewRecord && isset($manipulation[$baseTable])) {
$manipulation[$baseTable]['command'] = 'update';
}
DB::manipulate($manipulation);
if(isset($isNewRecord) && $isNewRecord) {
DataObjectLog::addedObject($this);
} else {
DataObjectLog::changedObject($this);
}
$this->onAfterWrite();
$this->changed = null;
} elseif ( $showDebug ) {
echo "<b>Debug:</b> no changes for DataObject<br />";
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
}
// Clears the cache for this object so get_one returns the correct object.
$this->flushCache();
if(!isset($this->record['Created'])) {
$this->record['Created'] = SS_Datetime::now()->Rfc2822();
}
$this->record['LastEdited'] = SS_Datetime::now()->Rfc2822();
} else {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
}
// Write ComponentSets as necessary
if($writeComponents) {
$this->writeComponents(true);
}
return $this->record['ID'];
}
Look at the $this->onAfterWrite();
It probably goes to my own function on NewsArticle.php and there starts the loop! I'm not sure though, so i could need some help!!
Does anyone knows how to use the doPublish() function?
The reason that is happening is that in the DataObjectAsPage::publish() method, it is calling ->write() - line 11 of your 3rd code sample.
So what happens is it calls ->write(), at the end of ->write() your onAfterWrite() method is called, which calls publish(), which calls write() again.
If you remove the onAfterWrite() function that you've added, it should work as expected.
The doPublish() method on DataObjectAsPage will take care of publishing from Stage to Live for you.
Related
I am trying to check whether a generated string is in my database table or not.
If there is no duplicate I want to return and save it.
If there is a duplicate I want to generate a new string and save it
currently this function does not return me the generated token:
public function checkForHashDuplicate($string)
{
$newToken = '';
$inquiries = DB::table('inquiries')->get();
foreach($inquiries as $inquiryKey => $inquiryValue)
{
while($inquiryValue->android_device_token == $string)
{
$newToken = generateAlphanumericString();
if($newToken != $inquiryValue->android_device_token)
{
return $newToken;
}
}
}
}
$inquiry->android_device_token = $this->checkForHashDuplicate($hash);
A simplified version of your code is:
public function checkForHashDuplicate($string)
{
do {
// generate token
$newToken = generateAlphanumericString();
// find token in a DB
$duplicate = DB::table('inquiries')
->where('android_device_token', '=', $newToken)->first();
// if token is FOUND `$duplicate` has truthy value
// and `do-while` keeps running
// else `$duplicate` is falsy and `do-while` breaks
} while ($duplicate);
return $newToken;
}
Instead of using:
$inquiries = DB::table('inquiries')->get();
and loop, use where like:
$inquiries = DB::table('inquiries')->where('android_device_token', '=', $string)->get();
if( count($inquiries ) == 1 )
{
// found
}
else
{
// Not found, generate new one
}
You can check like this:
public function checkDuplicate($string){
$count = DB::table('inquiries')->where('android_device_token',$string)->count();
if($count==0){
// $string exists
return generateAlphanumericString();
}else{
// $string doesn't exists
return $string;
}
}
Now, use this function to assign value of $string or new token as
$inquiry->android_device_token = $this->checkForHashDuplicate($hash);
Hope you understand.
Creating a block builder that loops through blocks pulled form database in order.
if( loop_blocks()) {
while( loop_blocks()) {
if( have_block('standard-content-block')) {
echo 'standard-content-block';
}
elseif( have_block('executive-intro-block')) {
echo 'executive intro block';
}
}
}
My function loop_blocks pulls the blocks from the database in order and set the array as a global variable:
function loop_blocks() {
global $db;
$page_id = get_page_id();
$GLOBALS['loop_position'] = 0;
$loop_position = $GLOBALS['loop_position'];
$stm = $db->prepare("SELECT * FROM page_blocks WHERE page_id = :id ORDER BY `block_order` ASC");
$stm->execute(array(':id' => $page_id));
$res = $stm->fetchAll();
$GLOBALS['block_loop'] = $res;
if(!$res) {
return false;
} elseif(!$GLOBALS['block_loop'][$loop_position]) {
return false;
} else {
return true;
}
}
The function have_block gets the current loop position and determines whether the name as determined, exists in the array and increases the loop position:
function have_block($block_name) {
$loop_position = $GLOBALS['loop_position'];
if(!$GLOBALS['block_loop'][$loop_position]) {
return false;
} elseif(!$GLOBALS['block_loop'][$loop_position][block_name] = $block_name) {
return false;
} else {
$GLOBALS['loop_position'] = $loop_position+1;
return true;
}
}
This returns an infinite loop however and I can't figure out a way to move the while loop onto the next step?
EDIT I'm using a while loop because the function have_block will set-up a global variable for the current block id. This will then be used within a function called the_element. Such as:
if( loop_blocks()) {
while( loop_blocks()) {
if( have_block('standard-content-block')) {
the_element('heading');
}
}
}
If I don't use the function have_block to set this up, then I'd need to pass the block id from the foreach loop into every element as a second argument.
I fixed this by, as #Jim, noted I was re-setting the loop_position within loop_blocks() which was why the while loop was repeating infinitely. It was then a simple case of an error when typing:
} elseif(!$GLOBALS['block_loop'][$loop_position][block_name] = $block_name) {
return false;
Should have been:
} elseif($GLOBALS['block_loop'][$loop_position][block_name] != $block_name) {
return false;
Note that I had the exclamation point in the incorrect place.
This now works perfectly as I need.
I am trying to get an if statement to dynamically code itself based on user input. So the if statement code is being inserted into a variable ($if_statement_variable), like this:
$if_statement_variable = "if (";
$price = trim($_GET["Price"]);
if (!empty($price)) {
$if_statement_variable .= " $Product->price < $price ";
}
$product_name = trim($_GET["Product_name"]);
if (!empty($product_name)) {
$if_statement_variable .= " && $Product->$product_name == 'product_name_string' ";
}
// plus many more if GET requests
$if_statement_variable .= ") ";
Then results from an XML file will be displayed based on user values submitted and the $if_statement_variable.
$XMLproducts = simplexml_load_file("products.xml");
foreach($XMLproducts->product as $Product) {
echo $if_statement_variable; // Here is where the problem is
{ // opening bracket for $variable_if_statement
echo $Product->$product_name; // products displayed based on if statement code in $if_statement_variable
} //closing bracket for $variable_if_statement
}
The echo $if_statement_variable above correctly displays $price from this variable string, but does NOT display $Product->price. Assuming $price had a value of 1000, the output is if ( == 1000 ). How can I get $Product->price to correctly insert itself into the $if_statement_variable so that it displays the $Product->price values from the XML file?
If you're trying to generate a boolean value dynamically, based on some complicated logic, just assign the true/false value to a variable, (say, $booleanValue) and then do if($booleanValue){}
Something like:
$price = trim($_GET['price']);
$product_name = trim($_GET['Product_name']);
if(!empty($price)){
$booleanValue = ($Product->price < $price);
}
if(!empty($productName)){
$booleanValue = ($booleanValue && $Product->$product_name == 'product_name_string')
}
if($booleanValue){
echo $Product->$product_name;
}
In other words, create a variable to hold the actual boolean value, not a string to hold an expression that will evaluate to a boolean value.
Do not build PHP source as a string. In this case callables are a better solution. A callable is a function inside a variable. In PHP this might be an function name, and array with an object and a method name, an anonymous function or an object implementing invoke.
Here is an example for anonymous functions:
function getCondition($parameters) {
$conditions = [];
if (isset($parameters['Price']) && trim($parameters['Price']) != '') {
$price = trim($parameters['price']);
$conditions[] = function($product) use ($price) {
return $product->price < $price;
}
}
if (isset($parameters['Product_name']) && trim($parameters['Product_name']) != '') {
$productName = trim($parameters['Product_name']);
$conditions[] = function($product) use ($productName) {
return $product->product_name == $productName;
}
}
return function($product) use ($conditions) {
foreach ($conditions as $condition) {
if (!$condition($product)) {
return FALSE;
}
}
return TRUE;
}
}
$condition = getConditon($_GET);
if ($condition($product)) {
...
}
It is important that each function can be called the same way. So if you call the condition function you not need to know, which condition it is. In the example above you can imagine that the getCondition() function can get really complex really fast if you add additional conditions.
If you encapsulate the conditions into classes, the usage becomes more readable:
$condition = new \YourCompany\Product\Conditions\Group(
new \YourCompany\Product\Conditions\PriceMaximum($_GET, 'Price'),
new \YourCompany\Product\Conditions\Name($_GET, 'Product_name')
);
if ($condition($product)) {
...
}
This way you separate the actual condition logic from the from the use. The source of all classes is some more then the anonymous function variant. But you you can put each class in it's own file and use them in any combination you need.
The classes need to implement __invoke().
class Group {
private $_conditions = array();
public function __construct() {
$this->_conditions = func_get_args();
}
public function __invoke($product) {
foreach ($this->_conditions as $condition) {
if (!$condition($product)) {
return FALSE;
}
}
return TRUE;
}
}
class Name {
private $_productName = NULL;
public function __construct($parameters, $name) {
if (isset($parameters[$name]) && trim($parameters[$name]) > 0) {
$this->_productName = trim($parameters[$name]);
}
}
public function __invoke($product) {
return (
NULL === $this->_productName ||
$product->product_name == $this->_productName
);
}
}
class PriceMaximum {
private $_maximum = NULL;
public function __construct($parameters, $name) {
if (isset($parameters[$name]) && trim($parameters[$name]) > 0) {
$this->_maximum = trim($parameters[$name]);
}
}
public function __invoke($product) {
return (
NULL === $this->_maximum ||
$product->price < $this->_maximum
);
}
}
This concept can even be used together with an anonymous function:
$condition = new \YourCompany\Product\Conditions\Group(
new \YourCompany\Product\Conditions\PriceMaximum($_GET, 'Price'),
new \YourCompany\Product\Conditions\Name($_GET, 'Product_name'),
function ($product) {
return $product->category == 'food';
}
);
I've been given this bit of code:
if(isset($_GET['viewevent'])) {
if(count($_SESSION['e_lastviewed']) == 0) {
$_SESSION['e_lastviewed'][0] = $_GET['viewevent'];
} else if(!in_array($_GET['viewevent'], $_SESSION['e_lastviewed'])) {
$_SESSION['e_lastviewed'][2] = $_SESSION['e_lastviewed'][1];
$_SESSION['e_lastviewed'][1] = $_SESSION['e_lastviewed'][0];
$_SESSION['e_lastviewed'][0] = $_GET['viewevent'];
}
}
if($_GET['show']) {
$_SESSION['show'] = $_GET['show'];
} else if($_SESSION['show']=='') {
$_SESSION['show'] = "all";
}
It apparently saves ID's of recently viewed items, so i need to put these id's into an array.
Would this work?
$my_array = array($_SESSION['e_lastviewed'][2],$_SESSION['e_lastviewed'][1],$_SESSION['e_lastviewed'][0]);
I've ran it but it displays blank results (not sure if thats due to me not doing it right or incomplete code...Have i missed something? I'm not sure if i completley understand the script i was given...
try this:
if ( !isset($_SESSION['e_lastviewed']) )
$_SESSION['e_lastviewed'] = array();
// alt: while(count($_SESSION['e_lastviewed']) > 2 ) {
if(count($_SESSION['e_lastviewed']) > 2 ) {
array_shift($_SESSION['e_lastviewed']); // drop off from 3
array_unshift($_SESSION['e_lastviewed'],$_GET['viewevent']); // insert in the beginning
if($_GET['show']) {
$_SESSION['show'] = $_GET['show'];
} else if($_SESSION['show']=='') {
$_SESSION['show'] = "all";
}
When I access the wrong function in my controller, a 404 error page is working well. But, when i access url like 'http://example.com/model/detail/116', which 116 is wrong number [are not in the database], my 404 error page not working.
I have this code in my controller:
public function detail()
{
$id['id_galeri'] = $this->uri->segment(3);
$detail = $this->app_model->getSelectedData("tbl_galeri",$id);
foreach($detail->result() as $d)
{
$bc['jdl'] = "View";
$bc['id_galeri'] = $d->id_galeri;
$bc['nama'] = $d->nama;
$bc['foto'] = $d->foto;
$bc['deskripsi'] = $d->deskripsi;
$bc['stts_input'] = "deskripsi";
}
if($this->uri->segment(3) == '' && $id['id_galeri'] == FALSE)
{
$segment_url = 0;
}else{
if(!is_numeric($this->uri->segment(3)) || !is_string($this->uri->segment(3))){
redirect('404');
}else{
$segment_url = $this->uri->segment(3);
}
}
$this->load->view('frontend/global/bg_top');
$this->load->view('frontend/page/bg_view_model',$bc);
$this->load->view('frontend/global/bg_footer');
}
Sorry for my bad english, please help :-)
Thank you..
Instead of:
redirect('404');
try using CodeIgniter's native:
show_404('page');
EDIT
Try this code, a bit cleaned up and the checks are done before they're saved for views use.
public function detail()
{
$id['id_galeri'] = $this->uri->segment(3);
// Check if the supplied ID is numeric in the first place
if ( ! is_numeric($id['id_galeri']))
{
show_404($this->uri->uri_string());
}
// Get the data
$detail = $this->app_model->getSelectedData("tbl_galeri",$id);
// Check if any records returned
if (count($detail->result()) === 0)
{
show_404($this->uri->uri_string());
}
foreach($detail->result() as $d)
{
$bc['jdl'] = "View";
$bc['id_galeri'] = $d->id_galeri;
$bc['nama'] = $d->nama;
$bc['foto'] = $d->foto;
$bc['deskripsi'] = $d->deskripsi;
$bc['stts_input'] = "deskripsi";
}
/**
* Here do whatever you want with the $segment_url which doesn't seem to be
* used in your code
*/
$this->load->view('frontend/global/bg_top');
$this->load->view('frontend/page/bg_view_model',$bc);
$this->load->view('frontend/global/bg_footer');
}