CakePHP Tags Model - php

I have a simple app that has Posts and Tags.
A post can have multiple tags and tags can belong to multiple posts. The basic DB schema is like this:
post table:
id
title
slug
content
tag table:
id
title
slug
tag_posts table:
id
tag_id
post_id
So when a user saves a post it will take a list of tags in a field and then first check if the tags exist. If the tag doesn't exist, then create them, or if not get there existing ids. (all tags are lower-cased so you can't have Tag and tag). The tags are then linked to the post by adding the ids into the tag_posts table.
This works so far, but the code to do it is really horrible. I have the following method in the Tag model which takes the list of tags and does the above:
public function savePostTags($postId, $tags)
{
// Explode the topics by comma, so we have an array to run through
$tags = explode(',', $tags);
// Array for collecting all the data
$collection = array();
//debug($tags);
function is_array_empty( $mixed ) {
if ( is_array($mixed) ) {
foreach ($mixed as $value) {
if ( ! is_array_empty($value) ) {
return false;
}
}
} elseif ( ! empty($mixed) ) {
return false;
}
return true;
}
if(is_array_empty($tags) ) {
return false;
}
// First of all we bin off existing associations to make sure we don't duplicate
// NOTE: The false means don't delete the topics or posts related!!! VERY IMPORTANT!
$this->TagPost->deleteAll(array('TagPost.post_id' => $postId), false);
$tags = array_unique($tags);
// Make sure all tags are unique
foreach($tags as $tag)
{
// Trim it so remove unwanted white spaces in the beginning and the end.
$tag = trim($tag);
// If tag is empty exit here
if(empty($tag) ) {
return false;
}
// Make it all lowercase for consistency of tag names
$tag = strtolower($tag);
// Check if we already have a topic like this
$controlFind = $this->find(
'first',
array(
'conditions' => array(
'title' => $tag
)
)
);
//debug($controlFind);
// No record found (create new tag and link it up)
if(!$controlFind)
{
$this->create();
if(
!$this->save(
array(
'title' => $tag,
'slug' => Inflector::slug($tag)
)
)
)
{
// If only one saving fails we stop the whole loop and method.
return false;
}
else
{
$temp = array(
'TagPost' => array(
'tag_id' => $this->id,
'post_id' => $postId
)
);
}
}
else // Or if found link it with the post
{
$temp = array(
'TagPost' => array(
'tag_id' => $controlFind['Tag']['id'],
'post_id' => $postId
)
);
}
$collection[] = $temp;
}
return $this->TagPost->saveAll($collection, array('validate' => false));
}
Any ideas on how to refactor this?
As it feels really long-winded and seems to break the conventions of CakePHP.

Try this
public function savePostTags($postId, $tags)
{
$tags = explode(',', $tags);
foreach($tags as $key=>$value){
$listTags[$key] = trim(strtolower($value));
}
# find the list of existing tags
$listOfExistingTags = $this->find('list',
array('conditions' => array('title' => $tags),
'recursive' => -1));
$newTags = array_diff($tags, $listOfExistingTags);
#save new tags
foreach($newTags as $key=>$value){
$saveData[$key]['Tag']['title'] = $value;
$saveData[$key]['Tag']['slug'] = Inflector::slug($value);
}
if(!empty($saveData))$this->saveAll($saveData);
# this save all the tags
# now save data in tag_posts
$listOfExistingTags = $this->find('list',
array('conditions' =>array('title' => $tags),
'fields'=>array('id','title'),
'recursive' => -1));
$i = 0;
foreach($listOfExistingTags as $key=>$value){
$tagPostData[$i]['TagPost']['tag_id'] = $key;
$tagPostData[$i]['TagPost']['post_id'] = $postId; $i++;
}
if(!empty($tagPostData)){
App::import('model','TagPost');
$TagPost = new TagPost();
$TagPost->saveAll($tagPostData);
}
}

Any ideas on how to refactor this?
Use the CakeDC Tags plugin. It is unit tested and easy to throw in, takes ~15mins. See it's readme.md. Its basically just adding the behavior and adding a field "tags" to your form.

Related

Dynamic array associating a custom field and a name

Working with wordpress function.
I have to make a function which is already made with static info, to be dynamic.
I am creating a custom field so that user can insert that info.
I need to pass an array of links associated with a name, which would be perhaps some post name.
The current code has the following structure:
function my_function() {
$First_link = 'http://www.myurl.com.br';
$Second_link = 'http://www.mysecondurl.com.br';
...etc
$allthose_links = array(
$First_link => 'First Quiz',
$Second_link => 'Second Quiz',
...etc
);
return $allthose_links;
}
I need this to be something like:
function my_function() {
$posts = get_posts($args);
$allthose_links[] = array();
foreach( $posts as $post ) {
$thelink = get_field( $post=>['mylink']);
$thename = $post->name;
}
$allthose_links = array( //here's the deal
$thelink => $thename
)
return $allthose_links;
}
The $allthose_link array should be inside the foreach loop so that each of the links will be added to $allthose_link array on every iteration using the link as the key. Your new function should look like the example below:
function my_function() {
$posts = get_posts($args);
$allthose_links = array();
foreach( $posts as $post ) {
$thelink = get_field( $post=>['mylink']);
$thename = $post->name;
$allthose_links[$thelink] = $thename;
}
return $allthose_links;
}

Exchange HTML in for AMP Pages

Is there a way to change HTML in AMP Pages? Example:
Make every <span class="example1">...</span> to ...
Or even better, change specific Shortcodes in Wordpress to ...?
I found a solution to switch shortcodes:
// AMP change thrive-shortcode to landingpage link
function add_shortcode_amp($content) {
if (function_exists( 'is_amp_endpoint' ) && is_amp_endpoint()) {
/// Define shortcode-name
$mb_shortc = 'thrive_2step';
/// Define Mappings (thrive_2step id => Target URL for links in AMP)
$ampMappings = array(
"17503" => 'https://www.test.de/schreibtisch-workout-2',
"17505" => 'https://www.test.de/merkmale-arbeitsplatz-kostenlos',
"17506" => 'https://www.test.de/hoehenverstellbarer-schreibtisch-rentenverischerung-antrag');
/// Init Regex arrays
$mb_rexp = array();
$subst = array();
foreach ($ampMappings as $key => $value) {
$mb_rexp[] = '/\[('.$mb_shortc.').*?.id=[\'"]'.$key.'[\'"].*?\](.*?)\[\/\1\]?/';
$subst[] = '${2}';
}
/// Process Content
return preg_replace($mb_rexp, $subst, $content);
}
return $content;
}
add_filter( 'the_content', 'add_shortcode_amp', 6);
function mbtest_hello_world() {
return '<a>Hello World</a>';
}
add_shortcode('AMPTEST', 'mbtest_hello_world');
function shortcode_switch4amp( $atts) {
extract( shortcode_atts( array(
'regular' => 'regular',
'amp' => 'amp'
), $atts ) );
if (function_exists( 'is_amp_endpoint' ) && is_amp_endpoint()) {
return do_shortcode(str_replace(array("{","}"), array("[","]"),$amp));
} else {
return do_shortcode(str_replace(array("{","}"), array("[","]"), $regular));
}
}
add_shortcode('switch4amp', 'shortcode_switch4amp');

If array value begins with

I have an array of values.
My crawler scans the web page and inserts all the links, the links' titles and description is a multidimensional array.
But now I have a new array and I only want the links, descriptions and titles etc. if they begin with any value in the array ($bbc_values)
But I don't really know how to do this. I have have gotten pretty far in terms of the actual code but can anyone give me any ideas a) why my code isn't working b) suggestions for my problem?
$bbc_values = array('http://www.bbc.co.uk/news/health-', 'http://www.bbc.co.uk/news/politics-', 'http://www.bbc.co.uk/news/uk-', 'http://www.bbc.co.uk/news/technology-', 'http://www.bbc.co.uk/news/england-', 'http://www.bbc.co.uk/news/northern_ireland-', 'http://www.bbc.co.uk/news/scotland-', 'http://www.bbc.co.uk/news/wales-', 'http://www.bbc.co.uk/news/business-', 'http://www.bbc.co.uk/news/education-', 'http://www.bbc.co.uk/news/science_and_enviroment-', 'http://www.bbc.co.uk/news/entertainment_and_arts-', 'http://edition.cnn.com/');
foreach ($links as $link) {
$output = array(
"title" => Titles($link), //dont know what Titles is, variable or string?
"description" => getMetas($link),
"keywords" => getKeywords($link),
"link" => $link
);
if (empty($output["description"])) {
$output["description"] = getWord($link);
}
}
$data = implode( " , ", $output['link']);
foreach ($output as $new_array) {
if (in_array($output, $bbc_values)) {
$news_stories[] = $new_array;
}
var_dump($news_stories);
}
Okay, I don't completely understand the code here.
But I think $output array should be declared outside the first foreach loop and each array should be appended to it?
Because from the code you're writing, only the details of last $link will be stored inside the $output
Also, what is $data here? what are you using it for?
Turn $bbc_values into a regex:
$bbc_re = '/^('.implode('|', array_map('quotemeta', $bbc_values)).')/';
Then use this regex to filter the links.
foreach ($links as $link) {
if (preg_match($bbc_re, $link)) {
/* Do stuff with $link */
}
}
I assume you what you want is to have an array with links that starts with of the links in the bbc_values and additionally a string $data with a comma separated list of all links. Try something this :
<?php
$bbc_values = array('http://www.bbc.co.uk/news/health-', 'http://www.bbc.co.uk/news/politics-', 'http://www.bbc.co.uk/news/uk-', 'http://www.bbc.co.uk/news/technology-', 'http://www.bbc.co.uk/news/england-', 'http://www.bbc.co.uk/news/northern_ireland-', 'http://www.bbc.co.uk/news/scotland-', 'http://www.bbc.co.uk/news/wales-', 'http://www.bbc.co.uk/news/business-', 'http://www.bbc.co.uk/news/education-', 'http://www.bbc.co.uk/news/science_and_enviroment-', 'http://www.bbc.co.uk/news/entertainment_and_arts-', 'http://edition.cnn.com/');
$news_stories = array();
$all_links = array();
$news_links = array();
foreach ($links as $link) {
$item = array(
"title" => Titles($link),
"description" => getMetas($link),
"keywords" => getKeywords($link),
"link" => $link
);
if (empty($item["description"])) {
$item["description"] = getWord($link);
}
foreach($bbc_values as $bbc_value) {
// note the '===' . this is important
if(strpos($item['link'], $bbc_value) === 0) {
$news_stories []= $item;
$news_links []=$item['link'];
break;
}
}
$all_links[] = $item['link'];
}
$data_all_links = implode(' , ', $all_links);
$data_news_links = implode(' , ', $news_links);
var_dump($news_stories);

Converting array into multidimensional array in PHP when result is send from Oracle

Here is example how my array should look like:
$library = array(
'book' => array(
array(
'authorFirst' => 'Mark',
'authorLast' => 'Twain',
'title' => 'The Innocents Abroad'
),
array(
'authorFirst' => 'Charles',
'authorLast' => 'Dickens',
'title' => 'Oliver Twist'
)
)
);
When I get results from oracle database:
$row = oci_fetch_array($refcur, OCI_ASSOC+OCI_RETURN_NULLS);
But when I execute my code I only get one row.
For example: <books><book></book><name></name></books>
But I want all rows to be shown in xml.
EDIT:
This is my class for converting array to xml:
public static function toXml($data, $rootNodeName = 'data', &$xml=null)
{
// turn off compatibility mode as simple xml throws a wobbly if you don't.
if (ini_get('zend.ze1_compatibility_mode') == 1)
{
ini_set ('zend.ze1_compatibility_mode', 0);
}
if (is_null($xml))
{
$xml = simplexml_load_string("<".key($data)."/>");
}
// loop through the data passed in.
foreach($data as $key => $value)
{
// if numeric key, assume array of rootNodeName elements
if (is_numeric($key))
{
$key = $rootNodeName;
}
// delete any char not allowed in XML element names
$key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key);
// if there is another array found recrusively call this function
if (is_array($value))
{
// create a new node unless this is an array of elements
$node = ArrayToXML::isAssoc($value) ? $xml->addChild($key) : $xml;
// recrusive call - pass $key as the new rootNodeName
ArrayToXML::toXml($value, $key, $node);
}
else
{
// add single node.
$value = htmlentities($value);
$xml->addChild($key,$value);
}
}
// pass back as string. or simple xml object if you want!
return $xml->asXML();
}
// determine if a variable is an associative array
public static function isAssoc( $array ) {
return (is_array($array) && 0 !== count(array_diff_key($array, array_keys(array_keys($array)))));
}
}
?>
Now with below responde I have tried problem is I get following output: <book>...</book> tags after each row.. then I tried 3 dimensional array now I get: <book><book>...</book></book> on the proper place but I have 2 of them.
This is the line where I have determine which is root on that array and that's why I get this output. But don't know how to change it : $xml = simplexml_load_string("<".key($data)."/>");
Thank you.
oci_fetch_array() will always return a single row, you need to call it until there are no more rows to fetch in order to get all of them:
while ($row = oci_fetch_array($refcur, OCI_ASSOC+OCI_RETURN_NULLS))
{
$library['book'][] = $row;
}

PHP adding foreach to my array

I would like to append html to an item in my array before echoing it on my page and am unsure how to go about doing it.
My data is put into an array like so:
$query = $this->db->get();
foreach ($query->result() as $row) {
$data = array(
'seo_title' => $row->seo_title,
'seo_description' => $row->seo_description,
'seo_keywords' => $row->seo_keywords,
'category' => $row->category,
'title' => $row->title,
'intro' => $row->intro,
'content' => $row->content,
'tags' => $row->tags
);
}
return $data;
I would like to perform the following on my 'tags' before returning the data to my view:
$all_tags = explode( ',' , $row->tags );
foreach ( $all_tags as $one_tag ){
echo '' . $one_tag . '';
The reason for doing this is that the tags in my database contain no html and are simply separated by commas like so news,latest,sports and I want to convert them into
sports ...
My reason for doing this here rather than when I echo the data is that I don't want to repeat myself on every page.
You could just create a function to be used everyhwere you are including tags in your output:
function formatTags($tags) {
$tmp = explode(',', $tags);
$result = "";
foreach ($tmp as $t) {
$result .= sprintf('%s',
urlencode(trim($t)), htmlentities(trim($t)));
}
return $result;
}
And whenever you do something like echo $tags; you do echo formatTags($tags); instead. View code should be separated from model code, which is why I would advise to not put HTML inside your array.
Well first of all you're overwriting $data with every run of the loop so only the final result row will be listed.
Once that's out of the way (fix with $data[] = ...), try this:
...
'tags' => preg_replace( "/(?:^|,)([^,]+)/", "$1", $row->tags);
...

Categories