PHP: display data retrieved from database (Silverstripe CMS) - php

I have a problem using Silverstripe CMS.
I have basic SQL query of which results I would like to display in <ul><li> menu. It should be really easy but I failed while browsing documentation. I found some more complex examples (Project, Students), but it seems to me that should exists some easier way to do this. Can anyone help or please give me a hint?
To sum up: I would like to run a db sql query and display data as list.
My WishPage.php
class WishPage extends Page {
// some code which is irrelevant for this matter, but works fine
}
class WishPage_Controller extends Page_Controller {
private static $allowed_actions = array('AddNewWishForm');
public function AddNewWishForm() {
// here you can add data to db
}
// Now I would like to show it; and here is the problem.
public function ShowWishList() {
$records = DB::query ("SELECT Title FROM Wishes;");
return $records;
}
}
My template WishPage.ss
<% include SideBar %>
<div class="content-container unit size3of4 lastUnit">
<div id="WishForm">
<h2>Try it</h2>
$AddNewWishForm
</div>
</div>
Update
My Wishes.php
<?php
class Wishes extends DataObject {
private static $db = array(
'Name' => 'Text',
'Comment' => 'Text'
);
}

You can just use the SS built-in functionality to fetch the entries from the DB:
// returns the list of all wishes
public function ShowWishList() {
return Wishes::get();
}
and in the frontend you can just:
<ul>
<% loop $ShowWishList %><li>$Name: $Comment</li><% end_loop %>
</ul>
This requires that you have a list of DataObjects of course.

Decision of spekulatius should work well.
If you wont try with SQLQuery
public function getWishes(){
$query = new SQLQuery();
$query->setFrom('Wishes')->setSelect('"Name", "Comment"');
//$query->addWhere('');
$result = $query->execute();
//Debug::show($result);
$list = ArrayList::create();
foreach($result as $row) {
$list->push($row);
}
return $list;
}
in theme
<ul>
<% loop $getWishes %><li>$Name: $Comment</li><% end_loop %>
</ul>

Related

SilverStripe 3.7: How to render template syntax or PHP output with out spaces?

How can I remove spaces output in my template? For example I have a $Brand some of these output as two words as the are in my php variables as TommyHilfiger. Output becomes "Tommy Hilfiger". This is great for front end display but how can I render it as TommyHilfiger or Tommy-Hifiger? I want to use them as css classes in my html. Like $Brand.Nospaces for example. Or does it need to be done in the PHP?
PHP
class ProductPage extends Page {
// Contact object's fields
public static $db = array(
'Brand' => 'Varchar(255)'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab("Root.Details",
new DropdownField("Brand", "Brand",
array(
"Adidas" => "Adidas",
"AmericanSportsTeams" => "American Sports Teams",
"United Colors of Benetton" => "United Colors of Benetton",
"Valentino" => "Valentino",
)
)
);
return $fields;
}
}
class ProductPage_Controller extends Page_Controller {
}
Template ProductPage.ss
<% loop Children %>
<li class="$Brand">
<a href="$Link">
<figure style="background-image: URL(<% loop $ProductImages.limit(1,1) %>$Fill(400,600).URL<% end_loop %>);">
<img src="<% loop $ProductImages.limit(1) %>$Fill(400,600).URL<% end_loop %>" alt="$Title"
class="snipcart-add-item"
data-item-id="P$ID $MenuTitle"
data-item-max-quantity="1"
data-item-stock="1"
data-item-name="$Title"
data-item-price="<% if $SalePrice %>$SalePrice<% else %>$Price<% end_if %>"
data-item-description="$Content"
data-item-image="<% loop $ProductImages.limit(1) %>$Pad(50,50).URL<% end_loop %>">
</figure>
<div id="pro-deets">
<h3>$Title</h3>
$Brand
</div>
</a>
</li>
<% end_loop %>
Maybe with:
$filter = URLSegmentFilter::create();
$className = $filter->filter($title);
I'm just not clear how to apply this to $Brand it would need to put in the template as $BrandNoSpace or something as I need to use $Brand with its spaces too for the displayed txt.
One simple approach would be to modify the string in the SilverStripe controller:
class ProductPage_Controller extends Page_Controller
{
/**
* returns the brand name as a class (with dash instead of space)
*
* #return string
*/
public function BrandAsClass()
{
return str_replace(' ', '-', $this->Brand);
}
}
and then call $BrandAsClass in your template:
<% loop Children %>
<li class="$BrandAsClass">
<a href="$Link">
<figure style="background-image: URL(<% loop $ProductImages.limit(1,1) %>$Fill(400,600).URL<% end_loop %>);">
<img src="<% loop $ProductImages.limit(1) %>$Fill(400,600).URL<% end_loop %>" alt="$Title"
class="snipcart-add-item"
data-item-id="P$ID $MenuTitle"
data-item-max-quantity="1"
data-item-stock="1"
data-item-name="$Title"
data-item-price="<% if $SalePrice %>$SalePrice<% else %>$Price<% end_if %>"
data-item-description="$Content"
data-item-image="<% loop $ProductImages.limit(1) %>$Pad(50,50).URL<% end_loop %>">
</figure>
<div id="pro-deets">
<h3>$Title</h3>
$Brand
</div></a>
</li>
<% end_loop %>
A custom function in your controller will be much easier for what you want.
class ProductPage_Controller extends Page_Controller
{
public function BrandNoSpaces()
{
return str_replace(' ', '-', $this->Brand);
}
}
In your template:
<li class="$BrandNoSpaces">
'United Colors of Benetton' becomes
<li class="United-Colors-of-Benetton">
We need to create a PHP function to convert our string as there is no inbuilt way to do this in a Silverstripe template.
Silverstripe has an inbuilt URLSegmentFilter class that strips certain characters from a string. We can use this to convert our string to a class name friendly string.
We can create a getBrandClass function in our ProductPage class to convert the Brand to a nice string with no spaces or special characters:
class ProductPage extends Page
{
public static $db = [
'Brand' => 'Varchar(255)',
];
// Other ProductPage class code
// ...
public function getBrandClass()
{
$urlSegmentFilter = URLSegmentFilter::create();
return $urlSegmentFilter->filter($this->Brand);
}
}
Then in our template we call $BrandClass like this:
<% loop $Children %>
<li class="$BrandClass">
This will convert American Sports Teams to american-sports-teams.
Note that the getBrandClass function must go in the ProductPage class and not in the ProductPage_Controller class. This is because in the template a different controller is being used.
If you just want to remove spaces from the beginning or end of a string use
strip($string)
if you want to remove all spaces use
str_replace(' ', '', $string)
if you want to change a space to a -
str_replace(' ', '-', $string)
if you want to remove all possible white spaces use
preg_replace('/\s+/', '', $string)
This is pretty much a duplicate of Display Multi Word Categories As 1 Word With Dashes
You can use an extension for this. This means that this method will be available for any varchar field.
mysite/code/extensions/VarcharDecorator.php
<?php
class VarcharDecorator extends Extension {
function Slugify() {
return FileNameFilter::create()->filter(trim($this->owner->value);
}
}
mysite/_config/extensions.yml
Varchar:
extensions:
- VarcharDecorator
Now you can use $Brand.Slugify in your templates.

Call to Member function on Array

So I'm utilizing a for each to try to generate a menu dynamically from a MySQL database. Because there's more than one it will always return an array. I use multiple files to generate the menu.
I use a class to create the menu
class menu extends db{
public function LoadMainMenu() {
global $db;
$query = <<<SQL
SELECT id,name
FROM menu
WHERE enabled = :active
AND location = :mainmenu
SQL;
$resource = $db->sitedb->prepare( $query );
$resource->execute( array(
':active' => '1',
':mainmenu' => '1',
));
foreach($resource as $row){
echo '<li>'.$row['name'].'</li>';
}
}
$menu = new menu();
My next file is my base.class.php file
function LoadMainMenu() {
global $menu;
$menu->LoadMainMenu();
}
Then I have my index.php file within my theme settings where I have it called
<ul class='topmenu'>
<?php LoadMainMenu(); ?>
</ul>
It then gives me the error that it's a call to a member function on an array on base.class.php; If anymore code would help please let me know.
Not Really an end all cure all, but it works. I added it within the db.class.php and then just added global $db and then called $db->GetMainMenu() within the main index.php Template and it seems to have worked. Other than that I'm not sure what else to do.

Silverstripe 3: create page control function to get images from grandchild pages and sort all randomly

I'm trying to get a specific image ($FeaturedImage) from every grandchild page (GalleryPage.ss) of my Portfolio Page (PortfolioPage.ss) and display them in a random order.
I can get the images using the template easily enough. PortfolioPage.ss
<% loop Children %>
<% loop Children %>
<% loop FeaturedImage %>
<img src="$Url"></>
<% end_loop %>
<% end_loop %>
<% end_loop %>
But this will display them in the order of the pages in the menu.
After some research it seems best to create a function in the page controller, but I'm not sure how to write this.. (anyone with a link to documentation / tutorials on these would also be great).
Examples of similar code found so far:
get Dataobjects from Children - SilverStripe 3.1
http://www.silverstripe.org/template-questions/show/23296
Silverstripe docs:
http://doc.silverstripe.org/framework/en/topics/datamodel
I'm just not sure how to apply this to my code..thanks
Basically you need to create a function in your Portfolio Page Controller (or in whatever page you need this logic to be).
Here are 2 examples. First one just gets all the existing FeaturedImage from the database and returns then in random order:
function AllFeaturedImages()
{
return FeaturedImage::get()->sort('RAND()');
}
And this one get all the FeaturedImage from the page's children's children and return them in random order:
function DescendantFeaturedImages()
{
$featuredImages = array();
foreach ($this->Children() as $child)
{
foreach ($child->Children() as $grandChild)
{
$images = $grandChild->FeaturedImage();
if ( $images )
{
$featuredImages = array_merge( $featuredImages, $images->toArray() );
}
}
}
shuffle($featuredImages);
return ArrayList::create($featuredImages);
}
If the FeaturedImage relation is just a has_one, this changes a little bit:
function DescendantFeaturedImages()
{
$featuredImages = array();
foreach ($this->Children() as $child)
{
foreach ($child->Children() as $grandChild)
{
$image = $grandChild->FeaturedImage();
if ( $image )
{
array_push( $featuredImages, $image );
}
}
}
shuffle($featuredImages);
return ArrayList::create($featuredImages);
}
Then in your Portfolio Page template you can just loop through the FeaturedImage by calling the function name. So here, either $AllFeaturedImages or $DescendantFeaturedImages. In your case you'll get something like:
<% loop $DescendantFeaturedImages %>
<img src="$URL"/>
<% end_loop %>
I could find one example in the SilverStirpe tutorials using a Controller function: http://doc.silverstripe.org/framework/en/tutorials/2-extending-a-basic-site
Let me know how this goes.
I tried Colymba's code, and it worked like a champ. I would recommend following his code over the method I'm gonna explain below.
As you say in a comment, you can access grandparent images from template. You can use JavaScript, or as in this example jQuery, to randomly sort your images.
(function($){
$.fn.shuffle = function() {
var allElems = this.get(),
getRandom = function(max) {
return Math.floor(Math.random() * max);
},
shuffled = $.map(allElems, function(){
var random = getRandom(allElems.length),
randEl = $(allElems[random]).clone(true)[0];
allElems.splice(random, 1);
return randEl;
});
this.each(function(i){
$(this).replaceWith($(shuffled[i]));
});
return $(shuffled);
};
})(jQuery);
And then call the function on the elements you want to sort randomly:
$('#imgholder img').shuffle();
A more thorough explanation can be found on css-tricks.com

Building a menu system with a PHP class

UPDATE 2 ::
This is the output I am getting from the function right now :
<nav>
<ul> //UL 1
<ul> //UL 2
menu link 1
<li>login</li>
<li>
<ul>sublink3</ul>
</li>
<li><a>menu link 3</a></li>
<li>
<ul>
sublink1
<li>sublink2</li>
</ul>
</li>
<li>menu link 4</li>
<li>menu link 5</li>
<li>
<ul>
sublink5a
<li>sublink5b</li>
<li>sublink5c</li>
<li>sublink5d</li>
</ul>
</li>
</ul> //End of UL 2
<ul></ul> //dont not why this is here?
</ul> //End of UL 1
</nav>
UPDATE
Ok I have re-move the construct, and this is how I am trying to display my menu :
<?php include('./includes/menu.php'); ?>
<h1>HEADER FILE TITLE TEST</h1>
<?php
$build = new Menu;
var_dump($build->DisplayMenu());
?>
I mainly only use CakePHP to build sites with, but I am trying to push my PHP skills up to the next level. So I am looking at other frameworks and OOP (which I have never used within PHP before). So I set myself a little task of building a menu system, controllable from a database e.g. titles and path links come form my db.
This works fine when I just built it has a function put all my menu system items within an array and then used a print call to display the menu, then I just called the function on the page I had required the file to.
But I thought that this was not the best way about doing it, so I wanted to make it a class, so I put a class call around my function, and then changed the print call to a return. What I got form the class/function was a NULL answer when I var dumped it. So I did some research, and re-read a lot about how to declare an array within the magic 'construct' function.
But now I am very confused, should this be inside my Menu function or outside? Just by adding the construct function, it started to display 'string(9)' - which I don't know why? Here is my code :
//Menu Include file
class Menu {
public $testforme = "dfdfdfdf"; //Just a test to see how to call somedata
public function DisplayMenu() {
$DBConn = getConnection(); //Set new database connection
$SQLMainMenu = "SELECT * FROM menu"; //Get Menu setting from database
//$MenuBuild[] = NULL; //Open array for menu to save data into
function __construct($MenuBuild = array()) {
//not sure what to put here???
}
try {
$MenuBuild[] = '<nav><ul>';
//Foreach loop for all main menu links
foreach ($DBConn->query($SQLMainMenu) as $MainMenu) {
$MainMenuID = $MainMenu['id'];
$MainMenuPath = $MainMenu['linkpath'];
$MainMenuSublinkCounts = $MainMenu['sublinks'];
$SQLSubMenu = "SELECT * FROM submenu WHERE menu_id = $MainMenuID";
if ($MainMenuPath == 'NULL') {
$MenuBuild[] = '<li><a>' .$MainMenu['name'] .'</a>';
} else {
$MenuBuild[] = '<li>' .$MainMenu['name'] .'';
}
if ($MainMenuSublinkCounts >=1) { $MenuBuild[] = '<ul>'; }
//Foreach loop to build all inner links for menu
foreach ($DBConn->query($SQLSubMenu) as $SubMenu) {
$SubLinkMenuIDs = $SubMenu['menu_id'];
$SubLinkTitles = $SubMenu['name'];
$SubLinkPaths = $SubMenu['linkpath'];
if ($SubLinkMenuIDs == $MainMenuID) {
$MenuBuild[] = '<li>'. $SubLinkTitles . '</li>'; }
} //End of inner foreach loop
if ($MainMenuSublinkCounts >=1) {
$MenuBuild[] = '</ul>';
}
$MenuBuild[] = '</li>';
} //End of foreach loop
$MenuBuild[] = '</ul></nav>';
//Print the Array that holds the menu.
foreach ($MenuBuild as $MenuKey => $MenuData) {
$MenuSystem = $MenuBuild[$MenuKey]; return $MenuSystem;
}
} catch(PDOException $e) {
echo $e->getMessage();
}
} //End of function DisplayMenu
} //end of class
Now I am connection to my db using a PDO, which is working fine, it within a other file that both are included on.
Please let me know of any good help sites and I have read lots of the questions / answers on here.
Also please go easy on me? this is the 1st time I am using OOP PHP.
If of I am not doing something right, with the way I am building the menu please point at any issues with that :).
Thanks Glenn.
Ok, this is for you, but you really need to do some more research before putting something like this up
my approach encapsulates some functionality but it should seem pretty clear where it's headed just by looking at it. Please feel free to make any questions
(REREedited) includes/menu.php
<?php
class Menu {
private $db;
private $main;
public function __construct($db=null) {
$this->db = $db ? $db : getConnection();
$q = 'SELECT id,name,linkpath,sublinks FROM menu';
$this->main = $this->db->query($q);
}
public function displayMenu() {
$items = array();
foreach ($this->main as $row) {
$path = $row['linkpath'];
$link = self::link($row['name'],$path==='NULL'?null:$path);
// if there is a submenu, concatenate to with the "parent" into the same "item"
if ($row['sublinks'] > 0) $link .= $this->subMenu($row['id']);
$items[] = $link;
}
// returns the whole nav menu.
return '<nav>'.self::lists(array_filter($items)).'</nav>';
}
private function subMenu($id) {
$q = 'SELECT name,linkpath FROM submenu WHERE menu_id = ';
$sub = array();
foreach ($this->db->query($q.$id) as $row) {
$sub[] = self::link($row['name'],$row['linkpath']);
}
return self::lists(array_filter($sub));
}
static function link($content, $href) {
return '<a '.($href?'href="'.$href.'"':'').'>' .$content.'</a>';
}
static function lists(array $items) {
if (!$items) return null;
return '<ul><li>'.implode('</li><li>',$items).'</li></ul>';
}
}
test.php
<?php include('./includes/menu.php'); ?>
<h1>HEADER FILE TITLE TEST</h1>
<?php
try {
$build = new Menu;
echo $build->displayMenu();
} catch (Exception $e) {
echo $e->getMessage().PHP_EOL.$e->getTraceAsString();
}
?>

where to process mysql queries in codeigniter?

Where should we process mysql queries in CodeIgniter application?
For example in a simple project we do like this :
for controller:
class Blog extends CI_Controller {
function posts(){
$data['query'] = $this->blog_model->index_posts();
$this->load->view('blog_view', $data);
}
}
and in view :
<?php
while ($post = mysql_fetch_object($query)):
?>
<div>
<p><?= $post->body; ?></p>
</div>
<?php endwhile; ?>
But, if we want to do something with body of post before print where should it be done?
For example, I want to write a function that formats the body of post and pass the body to it before doing echo.
Where should it be placed according to CoeIgniter's structure and recommended practices? (best option)
in the controller? (if so , how to use it)
in the view?
write a helper?
other approaches ?
Here's what is recommended:
Controller:
function posts() {
$this->load->model("blog_model");
$data['rows'] = $this->blog_model->index_posts();
$this->load->view("blog_view", $data);
}
Model: (blog_model.php)
function index_posts() {
$this->load->database();
$query = $this->db->get('your_table');
$return = array();
foreach ($query->result_array() as $line) {
$line['body'] = ... do something with the body....
$return[] = $line;
}
return $return;
}
View: (blog_view.php)
<?php foreach ($rows as $line): ?>
<div>
<p><?php echo $line['column']; ?></p>
</div>
<?php endforeach; ?>
Basically what happens is your model returns a multidimensional array that is passed the view and processed using a foreach() loop.
Good luck!
If you want to reuse that function create a helper. If you want this function only once put it in your controller and call from that controller.
Models are just for accessing database or maybe in few other cases, but mostly just for accessing things in database or editing, deleting etc. and sending the result to controller for further processing.
In your case I would stick with helper.
E.g. you will create a file top_mega_best_functions.php and put it inside helpers folder.
Than you write ther e.g. something like
function red_text($input) {
echo '<span style="color: red;">';
echo $input;
echo '</span>';
}
Then load the helper in your autoloader file or load before using.
And use in your view or controller like
$blablabla = "This text will be red";
red_text($blablabla);

Categories